Tim Schindler
Tim Schindler's Blog

Tim Schindler's Blog

CyberArk Central Policy Manager Platforms and Plugins with Continuous Deployment

Photo by Yancy Min on Unsplash

CyberArk Central Policy Manager Platforms and Plugins with Continuous Deployment

Tim Schindler's photo
Tim Schindler
·Jun 19, 2022·

12 min read

Subscribe to my newsletter and never miss my upcoming articles

Table of contents

CyberArk's platforms have what is called a Central Policy Manager (CPM) plugin which performs the credential change on the target system based on the settings defined in the platform. A CPM plugin can be simple as a script or as complicated as a custom-developed binary. Either way, sometimes these plugins need to be changed and thus their new versions distributed across the CyberArk landscape.

Distribution of a new version of a CPM plugin is typically just copying the files to each CPM but with the idea of continuous deployment using PACLI/PoShPACLI, GitHub Actions, and PowerShell we can have these new versions automatically deployed to all the CPMs whenever we commit a change to the version control repository that hosts our platforms and CPM plugins.

Note: More than anything this is a proof of concept and the tools leveraged were ones that I had the most experience with. The implementation should definitely be improved upon before moving into a production environment.

Deployment options for our continuous deployment workflow

Because deployment of a CPM plugin is just copying to the appropriate CPMs we can make the process as simple or as complex as we want it to be depending on the effort we want to spend.

  1. Use PowerShell's Copy-Item copy the CPM plugin files to each CPM. This is probably the easiest to execute but requires us to create and maintain a list of all our CPMs as target systems for the CPM plugin files. We are also responsible for any error handling in the case that a CPM is not available at the time of deployment.
  2. See how CyberArk distributes CPM plugin files when importing a platform package and see if we can leverage all or parts of that process. We would have CyberArk do a majority of the heavy lifting which hopefully results in less effort for us.
  3. Forego the idea altogether as we may be introducing more complexity than it is worth. Though a valid option it does not make for an interesting blog post!

As CyberArk has a mechanism for distributing CPM plugin files as part of the import process of a platform package (see step 3) and I do not like to re-invent the wheel, we will explore option 2.

Platforms and platform packages

The first step to seeing how our continuous deployment could leverage CyberArk's existing CPM plugin file deployment mechanism is understanding how the entire import platform package process works. And that starts with understanding a platform package.

According to the documentation a platform package is straightforward: it is just a Zip file that contains a CPM policy file (an INI file with the name Policy-$PolicyId.ini), a PVWA settings file (a XML file with the name Policy-$PolicyId.xml), and (optionally) the CPM plugin files (executables, PowerShell scripts, TPC prompts and process files, or a combination.)

Once created, the Zip file is imported through the PVWA and the CPM plugin files are copied automatically to the CPM.

Whereas the documentation does a good job telling us what to do, it does not provide enough details on how it is exactly done and this "how" is what we need to really understand if we want to leverage this process in our continuous deployment.

Understanding the import process of a platform's package

The best place to start is downloading a plugin from the CyberArk Marketplace, importing it, and observing the behavior.

I chose the RealVNC Service Mode as it is a TPC plugin using a PowerShell script with the classic prompts and process files, in addition to the required CPM policy and PVWA setting files, so we will see how those files are distributed.

image.png

Upon importing the platform package for the first time we see that it is done successfully and is added under Imported Platforms:

image.png

With the knowledge we already have from programmatically changing CyberArk platform properties using PowerShell we know that the PVWA settings file Policy-RealVNC.xml was likely merged into Policies.xml in the PVWAConfig safe and the CPM policy file Policy-RealVNC.ini was dropped into the Policies folder of the PasswordManagerShared safe but what happened to the CPM plugin files made up of the PowerShell script and the prompts and process files?

As with anything to do with platforms and their files, the first place to look should be the PasswordManagerShared safe.

First lets take a quick look to make sure the CPM policy file Policy-RealVNC.ini landed in the Policies folder like we expected:

image.png

What is interesting to note is that as part of the import process the CPM policy file was renamed from Policy-RealVNC.ini to Policy-RealVNCServiceMode.ini, which is the PolicyID of the plugin.

Moving on to finding the CPM plugins files, inside the same safe we see the ImportedPlatforms folder and a sub-folder named Policy-RealVNCServiceMode that contains our CPM plugin files. The sub-folder seems to be named based off the convention Policy-$PolicyId, which is something we should keep in mind for our CD workflow:

image.png

Step 3 of creating a platform package explains that the CPM plugin files will be dumped into the bin folder of the CPM. Checking the folder and pm.log we see that is the case:

image.png

The distribution of CPM plugin files is similar to that of what we see with the deployment of Universal Connectors on multiple PSMs. This works for the first deployment of a new platform but what about consecutive deployments for updated CPM plugins?

Updating CPM plugin files

Although we manually imported the platform through the PVWA we know that an API endpoint to do the same exists. The best idea when needing to update CPM plugin files would be to make the needed updates, re-create the platform package with the updated CPM plugin files, and use import platform package API endpoint to deploy the new version of the CPM plugin.

Unfortunately trying to import a package for a platform that shares the PolicyID or PolicyName of an existing platform is not possible. As the platform of an account is defined through an account property you could first delete the platform and then import it but at the very least any Master Policy exceptions are also deleted and need to be manually re-created.

We saw that as part of the import process our CPM plugin files were stashed in their own sub-folder under ImportedPlatforms in the PasswordManagerShared safe where they then ended up in the bin folder of the CPM. What happens when we upload a new version of an existing file directly into the Vault?

Uploading new CPM plugin files into the Vault

To test what happens we need to download one of the files out of PrivateArk, make an inconsequential change to it, and observe the results.

Inside of RealVNCProcess.ini we add a new comment to the file: image.png and then upload it back into the safe: image.png

After waiting some minutes, searching the pm.log for any log entry comes up with nothing:

image.png

and inside the bin folder RealVNCProcess.ini is still the same:

image.png

Out of curiousity, lets see what happens when we restart the CPM service:

image.png

Kicking the CPM service worked but having to restart the CPM service every time we want to deploy new CPM plugin files -- which could be countless times per day with our continuous deployment workflow -- across all our CPMs probably isn't what we want.

Looking again in pm.log when the plugin files are downloaded successfully, we always seeing a proceeding log message about a new policy being found. Can we trigger the downloading of new CPM plugin files by changing a superfluous value in the CPM policy file Policy-RealVNCServiceMode.ini and uploading that?

Manually triggering the download of new CPM plugin files by the CPM

We make another change to RealVNCProcess.ini:

image.png

and upload it to the Vault. We then head to the Policies folder in the PasswordManagerShared safe and download a copy of Policy-RealVNCServiceMode.ini.

But before we make any change to Policy-RealVNCServiceMode.ini lets simply upload that same exact copy back into the safe. This is worth trying because more than likely CyberArk is not comparing the content of files to know if there is an update rather just looking at the Last Modified date of the file -- which changes when the file is uploaded again.

image.png

Simply re-uploading Policy-RealVNCServiceMode.ini and thus it having a new Last Modified date was enough to trigger CyberArk to distribute our updated CPM plugin file. Along with our CPM plug files, we can stick the CPM policy file in our version control and distribute that at the same time we want to distribute new CPM plugin files.

Putting it all together in a continuous deployment workflow

I am using GitHub Actions with a self-hosted Windows runner so that PACLI can communicate with the Vault. Of course this can be done with Jenkins however I have less experience with Jenkins than I do with GitHub Actions -- which is practically zero.

All the code can be found in the below repo.

Setting up the GitHub repository

Still staying with the RealVNC CPM plugin, we create a repository on GitHub to store the platform files. Everything is committed and stored in the cpm folder -- not just the CPM plugin files -- because we need the CPM policy file to trigger the deployment of the CPM plugin files and in the future we may be able to do something with the PVWA settings file.

PS C:\Users\Tim\Projects> gci .\cyberark-extensions-continuous-delivery\


    Directory: C:\Users\Tim\Projects\cyberark-extensions-continuous-delivery


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         6/19/2022   1:40 PM                .github
d-----         6/19/2022   1:19 PM                cpm
-a----         6/19/2022  12:16 PM           1070 LICENSE
-a----         6/19/2022   1:22 PM            124 README.md
-a----         6/19/2022   1:18 PM           1483 Update-PASPlatformFiles.ps1


PS C:\Users\Tim\Projects> gci .\cyberark-extensions-continuous-delivery\cpm\


    Directory: C:\Users\Tim\Projects\cyberark-extensions-continuous-delivery\cpm


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         6/19/2022   1:20 PM            235 LICENSE
-a----          3/5/2020   3:48 PM            623 Policy-RealVNC.xml
-a----         6/19/2022   1:16 PM           6641 Policy-RealVNCServiceMode.ini
-a----          3/5/2020   2:30 PM           2566 realvnc.ps1
-a----         4/30/2020   3:35 PM           4163 RealVNCProcess.ini
-a----         3/13/2020   4:10 PM           1432 RealVNCPrompts.ini

Note that Policy-RealVNC.ini was renamed to Policy-RealVNCServiceMode.ini!

Inside the root of the repository, I have already created a PowerShell function in Update-PASPlatformFiles.ps1 that will do the heavy lifting of leveraging PACLI/PoShPACLI to get the platform files in the Vault as part of the continuous deployment workflow:

function Update-PASPlatformFiles {
    param (
        $PacliClientPath,
        $VaultAddress,
        $VaultCredential,
        $PlatformId,
        $CPMPolicyFile,
        $PVWASettingsFile,
        $Path
    )

    begin {
        Set-PVConfiguration -ClientPath $PacliClientPath
        Start-PVPacli
        New-PVVaultDefinition -vault $VaultAddress -address $VaultAddress -ErrorAction SilentlyContinue
        Connect-PVVault -user $($VaultCredential.UserName) -password $($VaultCredential.Password)
        Open-PVSafe -safe PasswordManagerShared
    }

    process {
        $Files = Get-ChildItem -Path $Path

        foreach ($File in $Files) {
            if ($File.Name -eq (Get-ChildItem $CPMPolicyFile).Name) {
                Add-PVFile -safe PasswordManagerShared -folder root\Policies -file $File.Name -localFolder $File.DirectoryName -localFile $File.Name
            } elseif ($File.Name -eq (Get-ChildItem $PVWASettingsFile).Name) {
               # Do something some day? Download Policies.xml, replace what is in Policies.xml with what is in PVWASettingsFile, then upload?
            } else {
                Add-PVFile -safe PasswordManagerShared -folder root\ImportedPlatforms\Policy-$PlatformId -file $File.Name -localFolder $File.DirectoryName -localFile $File.Name
            }
        }
    }

    end {
        Close-PVSafe -safe PasswordManagerShared
        Disconnect-PVVault
        Stop-PVPacli
    }
}

The last step is adding the password of the user you will connect to the Vault via PACLI with as a secret to the GitHub repository. Use VAULT_PASSWORD as the secret name otherwise you will need to adapt the workflow below with the correct secret name.

At this point we can push everything to GitHub.

Installing the GitHub Actions self-hosted runner

Using the GitHub Actions documentation we install a self-hosted runner and add it to the repository we created above.

It also makes sense to make sure PACLI and PoShPACLI are installed and working on the same machine the self-hosted runner is on.

Creating the GitHub Actions workflow

This is where the magic happens. As I am new to GitHub Actions we are using the default blank.yml workflow template you get when creating an action. We change the runs-on value to self-hosted so that the added self-hosted runner is used instead of a cloud-based machine. We also change the last steps so that it invokes my PowerShell function:

# This is a basic workflow to help you get started with Actions
name: CI
# Controls when the workflow will run
on:
  # Triggers the workflow on push or pull request events but only for the "main" branch
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  deploy:
    name: Deploy CPM files to the Vault
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v3
      - name: Uploads CPM files to the Vault
        shell: powershell
        run: |
          $env:HOMEDRIVE = ""
          $env:HomePath = "${{ runner.temp }}"
          Import-Module PoShPACLI
          $VaultCredential = New-Object System.Management.Automation.PSCredential ("Administrator", (ConvertTo-SecureString -String ${{ secrets.VAULT_PASSWORD }} -AsPlainText -Force))
          . .\Update-PASPlatformFiles
          Update-PASPlatformFiles `
            -PacliClientPath C:\PACLI\Pacli.exe `
            -VaultAddress 192.168.0.50 `
            -VaultCredential $VaultCredential `
            -PlatformId RealVNCServiceMode `
            -CPMPolicyFile .\cpm\Policy-RealVNCServiceMode.ini `
            -PVWASettingsFile .\cpm\Policy-RealVNC.xml `
            -Path .\cpm\

We need to overwrite $env:HOMEDRIVE and $env:HomePath as these do not seem to exist inside the runner. A PSCredential object is created with the VAULT_PASSWORD secret stored in the GitHub repository.

Then the Update-PASPlatformFiles function is imported and executed with arguments that match my environment. For now PlatformId, CPMPolicyFile, PVWASettingsFile, and Path are all hardcoded in the workflow but this can be changed later to support other/more platforms.

Making a change that will kick off the continuous deployment workflow

With the GitHub repository set up, self-hosted runner installed, and GitHub Actions workflow defined we are ready to see if it works.

We make a small change and commit it:

PS C:\Users\Tim\Projects\cyberark-extensions-continuous-delivery> git show 182fd804debb802d39999f6d5982d6f9de8dcd63
commit 182fd804debb802d39999f6d5982d6f9de8dcd63 (HEAD -> main, origin/main, origin/HEAD)
Author: Tim Schindler <tim.schindler@gmail.com>
Date:   Sun Jun 19 18:42:59 2022 +0200

    github actions-deployed change

diff --git a/cpm/RealVNCProcess.ini b/cpm/RealVNCProcess.ini
index aab2cd5..0d586a0 100644
--- a/cpm/RealVNCProcess.ini
+++ b/cpm/RealVNCProcess.ini
@@ -1,12 +1,13 @@
 ##########################################################################
 ###                    Vendor:                         Real VNC
-###                    Product:                        Real VNC - VNC Server - Service Mode
+###                    Product:                        Real VNC - VNC Server - Service Mode^M
 ###                    Version:                        6.4
 ###                    Plugin:                         v9.20200313.1
 ###                    Reference:                      00702774
 ###                    File:                           PROCESS
 ##########################################################################

+#GitHubActions^M

 [states]
 #Initialization
@@ -44,7 +45,7 @@ FailRegistryPath=FAIL(Cannot find Registry path because it does not exist, 8008)

 [transitions]
 #CurrentState                  Condition                                    NextState
-#------------                  ----------------                       --------------
+#------------                  ----------------                       --------------^M
 #############################################################################
 #                           INITIALIZATIONS
 #############################################################################
PS C:\Users\Tim\Projects\cyberark-extensions-continuous-delivery>

Upon seeing the successful execution of the GitHub Actions workflow we look at the CPM and see that the files were successfully deployed!

image.png

Going forward

We can see that it is (relatively) straightforward to have continuous delivery not only for our CPM plugin files but also the CPM platform settings (minus the PVWA settings -- for now.)

Spending some time to think about how our platform's files should be laid out within one or more version control repositories, improving the GitHub Actions workflow to be more flexible and do something like only push those files that have changed or only push files when there is a new tag, and not using the Vault Administrator account when connecting via PACLI can lead to mature continuous deployment for all of our platforms -- enabling the ability to treat them "as code!"

 
Share this