public/Invoke-VPASMetricsCPM.ps1

<#
.Synopsis
   RUN VARIOUS CPM METRICS FROM CYBERARK
   CREATED BY: Vadim Melamed, EMAIL: vmelamed5@gmail.com
.DESCRIPTION
   USE THIS FUNCTION TO GENERATE VARIOUS CPM RELATED METRICS FROM CYBERARK
.PARAMETER token
   HashTable of data containing various pieces of login information (PVWA, LoginToken, HeaderType, etc).
   If -token is not passed, function will use last known hashtable generated by New-VPASToken
.PARAMETER TargetMetric
   Specify which report will be run
   Possible values: CPMAssignedToSafes, CPMAssignedToAccounts, CPMAccountManagementStatus
.PARAMETER MetricFormat
   Specify the report output format
   NONE will return the generated hashtable of data that can be assigned to a variable
   Possible values: JSON, HTML, ALL, NONE
.PARAMETER OutputDirectory
   Specify where the location for report output to be saved
.PARAMETER HTMLChart
   Specify the HTML report type
   Possible values: BarGraph, LineGraph, PieChart, ALL
.PARAMETER HideRawData
   Removes the RawData visual from the exported output
   Helpful when exporting to a PDF or document to remove extra not needed information
.EXAMPLE
   $GenerateReport = Invoke-VPASMetricsCPM -TargetMetric CPMAssignedToSafes -OutputDirectory "C:\temp\VPASMetrics" -MetricFormat ALL -HTMLChart ALL
.OUTPUTS
   HashTable object if successful
   $false if failed
#>

function Invoke-VPASMetricsCPM{
    [OutputType([bool])]
    [CmdletBinding()]
    Param(

        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,HelpMessage="Enter TargetMetric to be generated (CPMAssignedToSafes, CPMAssignedToAccounts, CPMAccountManagementStatus)",Position=0)]
        [ValidateSet('CPMAssignedToSafes','CPMAssignedToAccounts','CPMAccountManagementStatus')]
        [String]$TargetMetric,

        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,HelpMessage="Enter ReportOutput type (JSON, HTML, ALL, NONE)",Position=1)]
        [ValidateSet('JSON','HTML','ALL','NONE')]
        [String]$MetricFormat,

        [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true,Position=2)]
        [String]$OutputDirectory,

        [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true,Position=3)]
        [ValidateSet('BarGraph','LineGraph','PieChart','ALL')]
        [String]$HTMLChart,

        [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true,Position=4)]
        [switch]$HideRawData,

        [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true,Position=5)]
        [hashtable]$token
    )

    Begin{
        $tokenval,$sessionval,$PVWA,$Header,$ISPSS,$IdentityURL,$EnableTextRecorder,$AuditTimeStamp,$NoSSL,$VaultVersion,$HideWarnings,$AuthenticatedAs,$SubDomain = Get-VPASSession -token $token
        $CommandName = $MyInvocation.MyCommand.Name
        $log = Write-VPASTextRecorder -inputval $CommandName -token $token -LogType COMMAND
    }
    Process{

        Write-Verbose "SUCCESSFULLY PARSED PVWA VALUE"
        Write-Verbose "SUCCESSFULLY PARSED TOKEN VALUE"
        Write-Verbose "SUCCESSFULLY PARSED TARGET METRIC VALUE: $TargetMetric"
        Write-Verbose "SUCCESSFULLY PARSED METRIC OUTPUT VALUE: $MetricFormat"

        try{
            if($MetricFormat -ne "NONE"){
                if([String]::IsNullOrEmpty($OutputDirectory)){
                    $curUser = $env:UserName
                    $OutputDirectory = "C:\Users\$curUser\AppData\Local\VPASModuleOutputs\Metrics"
                    Write-Verbose "NO OUTPUT DIRECTORY SUPPLIED, USING DEFAULT LOCATION: $OutputDirectory"

                    if(Test-Path -Path $OutputDirectory){
                        #DO NOTHING
                    }
                    else{
                        write-verbose "$OutputDirectory DOES NOT EXIST, CREATING DIRECTORY"
                        $MakeDirectory = New-Item -Path $OutputDirectory -Type Directory
                    }
                }
                else{
                    if(Test-Path -Path $OutputDirectory){
                        #DO NOTHING
                    }
                    else{
                        $curUser = $env:UserName
                        $OutputDirectory = "C:\Users\$curUser\AppData\Local\VPASModuleOutputs\Metrics"
                        write-verbose "$OutputDirectory DOES NOT EXIST, USING DEFAULT LOCATION: $OutputDirectory"
                        if(Test-Path -Path $OutputDirectory){
                            #DO NOTHING
                        }
                        else{
                            $MakeDirectory = New-Item -Path $OutputDirectory -Type Directory
                        }
                    }
                }
            }

            if([String]::IsNullOrEmpty($HTMLChart)){
                $HTMLChart = "BarGraph"
            }

            $htmlData = @{}
            if($TargetMetric -eq "CPMAssignedToSafes"){
                $tagout = "safes"
                #INITIALIZE HASH
                $OutputHash = @{}

                $AllSafes = Get-VPASAllSafes
                if(!$AllSafes){
                    Write-Verbose "FAILED TO RETRIEVE SAFES"
                    Write-VPASOutput -str "FAILED TO RETRIEVE SAFES" -type E
                    Write-VPASOutput -str $_ -type E
                    return $false
                }

                foreach($rec in $AllSafes.value){
                    $targetCPM = $rec.managingCPM
                    if([string]::IsNullOrEmpty($targetCPM)){
                        $targetCPM = "NONE"
                    }

                    if($targetCPM -match ","){
                        $targetCPMSPlit = $targetCPM -split ","
                        foreach($minirec in $targetCPMSPlit){
                            if($OutputHash.$minirec){
                                #$OutputHash.$minirec += 1
                                $OutputHash.$minirec.counter += 1
                                $OutputHash.$minirec.RawData += $rec
                            }
                            else{
                                $OutputHash += @{
                                    #$minirec = 1
                                    $minirec = @{
                                        counter = 1
                                        RawData = @($rec)
                                    }
                                }
                            }
                        }
                    }
                    else{
                        if($OutputHash.$targetCPM){
                            #$OutputHash.$targetCPM += 1
                            $OutputHash.$targetCPM.counter += 1
                            $OutputHash.$targetCPM.RawData += $rec
                        }
                        else{
                            $OutputHash += @{
                                #$targetCPM = 1
                                $targetCPM = @{
                                    counter = 1
                                    RawData = @($rec)
                                }
                            }
                        }
                    }
                }

                if($MetricFormat -eq "JSON" -or $MetricFormat -eq "ALL"){
                    $outputfile = "$OutputDirectory/CPMAssignedToSafes.json"
                    $OutputDataJSON = $OutputHash | ConvertTo-Json -Depth 100
                    Write-Output $OutputDataJSON | Set-Content $outputfile
                }
                if($MetricFormat -eq "NONE"){
                    $htmlData += @{
                        datahash = $OutputHash
                    }
                }
                if($MetricFormat -eq "HTML" -or $MetricFormat -eq "ALL"){
                    $outputfile = "$OutputDirectory/CPMAssignedToSafes.html"
                    $titlesplit = "CPMs Assigned To Safes"
                    $metricTag = "CPM Metrics"
                    $recommendation1 = "Assigning a CPM to a safe gives the ability to automate password rotation policies. The CPM ensures that passwords stored within the safe are regularly rotated according to predefined schedules or trigger events. This reduces the risk of password-based attacks, such as credential theft or misuse, by ensuring that passwords are frequently changed and not reused across multiple systems."
                    $recommendation2 = "Assigning a CPM to a safe enables organizations to define and enforce password complexity requirements at the safe level. Administrators can configure policies specifying minimum length, character types (uppercase, lowercase, digits, special characters, etc), and prohibited patterns. This ensures that passwords stored within the safe meet predefined strength criteria, making them more resilient to brute-force attacks and dictionary attacks."
                    $recommendation3 = "CPM enables organizations to enforce password history and reuse policies at the safe level. Administrators can configure policies specifying the number of previous passwords to retain, as well as restrictions on password reuse within a defined timeframe. This prevents users from reusing old passwords and enhances security by reducing the risk of password-based attacks"
                    $OutputDataJSON = $OutputHash | ConvertTo-Json -Depth 100
                    $curTime = get-date -Format "MM/dd/yyyy HH:mm:ss"
                    if($HTMLChart -eq "ALL"){
                        $tablestr = "Bar Graph, Line Graph, Pie Chart"
                    }
                    else{
                        $tablestr = $HTMLChart
                    }
                    $metricexplanation = "This metric tracks the number of safes different CPMs are assigned to<br>(Note - this metric will count a safe multiple times if multiple CPMs are assigned to the safe)"

                    $tempstr = ""
                    $tempstr2 = ""
                    $tempstr3 = ""
                    $tempstr4 = ""
                    $AllKeys = $OutputHash.Keys
                    $GetMinMax = @()

                    foreach($key in $AllKeys){
                        $curCount = $OutputHash.$key.counter
                        $GetMinMax += $curCount
                        $textColor =  '#{0:X6}' -f (Get-Random -Maximum 0x1000000)

                        $tempstr += "`"$key`","
                        $tempstr2 += "$curCount,"
                        $tempstr3 += "`"green`","
                        $tempstr4 += "`"$textColor`","
                    }

                    if($GetMinMax.count -ne 0){
                        $GetMinMax = $GetMinMax | Sort-Object
                        $mintick = 0
                        $maxtick = $GetMinMax[$GetMinMax.Count - 1]
                    }
                    else{
                        $mintick = 0
                        $maxtick = 0
                    }

                    if(![String]::IsNullOrEmpty($tempstr)){
                        $tempstr = $tempstr.Substring(0,$tempstr.Length-1)
                    }
                    if(![String]::IsNullOrEmpty($tempstr2)){
                        $tempstr2 = $tempstr2.Substring(0,$tempstr2.Length-1)
                    }
                    if(![String]::IsNullOrEmpty($tempstr3)){
                        $tempstr3 = $tempstr3.Substring(0,$tempstr3.Length-1)
                    }
                    if(![String]::IsNullOrEmpty($tempstr4)){
                        $tempstr4 = $tempstr4.Substring(0,$tempstr4.Length-1)
                    }

                    $htmlData += @{
                        Recommendation1 = $recommendation1
                        Recommendation2 = $recommendation2
                        Recommendation3 = $recommendation3
                        titlesplit = $titlesplit
                        outputfile = $outputfile
                        metricTag = $metricTag
                        OutputDataJSON = $OutputDataJSON
                        curTime = $curTime
                        tablestr = $tablestr
                        metricexplanation = $metricexplanation
                        HTMLChart = $HTMLChart
                        tempstr = $tempstr
                        tempstr2 = $tempstr2
                        tempstr3 = $tempstr3
                        tempstr4 = $tempstr4
                        datahash = $OutputHash
                        maxtick = $maxtick
                        mintick = $mintick
                    }
                }
            }
            if($TargetMetric -eq "CPMAssignedToAccounts"){
                $tagout = "accounts"
                #INITIALIZE HASH
                $OutputHash = @{}

                $AllSafes = Get-VPASAllSafes -IncludeAccounts
                if(!$AllSafes){
                    Write-Verbose "FAILED TO RETRIEVE SAFES"
                    Write-VPASOutput -str "FAILED TO RETRIEVE SAFES" -type E
                    Write-VPASOutput -str $_ -type E
                    return $false
                }

                foreach($rec in $AllSafes.value){
                    $safename = $rec.safeName
                    $targetCPM = $rec.managingCPM
                    $acctCount = $rec.accounts.count

                    if($safename -ne "PasswordManager_Pending"){
                        if($acctCount){
                            if([string]::IsNullOrEmpty($targetCPM)){
                                $targetCPM = "NONE"
                            }

                            if($targetCPM -match ","){
                                $targetCPMSPlit = $targetCPM -split ","
                                foreach($minirec in $targetCPMSPlit){
                                    if($OutputHash.$minirec){
                                        #$OutputHash.$minirec += $acctCount
                                        $OutputHash.$minirec.counter += $acctCount
                                        $OutputHash.$minirec.RawData += $rec
                                    }
                                    else{
                                        $OutputHash += @{
                                            #$minirec = $acctCount
                                            $minirec = @{
                                                counter = $acctCount
                                                RawData = @($rec)
                                            }
                                        }
                                    }
                                }
                            }
                            else{
                                if($OutputHash.$targetCPM){
                                    #$OutputHash.$targetCPM += $acctCount
                                    $OutputHash.$targetCPM.counter += $acctCount
                                    $OutputHash.$targetCPM.RawData += $rec
                                }
                                else{
                                    $OutputHash += @{
                                        #$targetCPM = $acctCount
                                        $targetCPM = @{
                                            counter = $acctCount
                                            RawData = @($rec)
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                if($MetricFormat -eq "JSON" -or $MetricFormat -eq "ALL"){
                    $outputfile = "$OutputDirectory/CPMAssignedToAccounts.json"
                    $OutputDataJSON = $OutputHash | ConvertTo-Json -Depth 100
                    Write-Output $OutputDataJSON | Set-Content $outputfile
                }
                if($MetricFormat -eq "NONE"){
                    $htmlData += @{
                        datahash = $OutputHash
                    }
                }
                if($MetricFormat -eq "HTML" -or $MetricFormat -eq "ALL"){
                    $outputfile = "$OutputDirectory/CPMAssignedToAccounts.html"
                    $titlesplit = "CPMs Assigned To Accounts"
                    $metricTag = "CPM Metrics"
                    $recommendation1 = "Having a CPM manage an account gives the ability for automatic password generation capabilities, allowing organizations to generate strong, random passwords for accounts stored within the safe. This eliminates the need for users to manually create passwords, reducing the risk of weak or predictable passwords being used."
                    $recommendation2 = "CPM provides the capability to enable a One-Time Password flow, which is a password that is for single-use or for a limited duration. This is commonly used to enhance an organization's security posture."
                    $recommendation3 = "Depending on the server specifications that the CPM is deployed to (assuming large implementation), a single CPM can handle about 100,000 accounts. An organization should consider deploying multiple CPMs to handle the workload for environments with large amount of accounts. Keep in mind, only one CPM should be assigned to a safe"
                    $OutputDataJSON = $OutputHash | ConvertTo-Json -Depth 100
                    $curTime = get-date -Format "MM/dd/yyyy HH:mm:ss"
                    if($HTMLChart -eq "ALL"){
                        $tablestr = "Bar Graph, Line Graph, Pie Chart"
                    }
                    else{
                        $tablestr = $HTMLChart
                    }
                    $metricexplanation = "This metric tracks the number of accounts different CPMs are managing<br>(Note - an account will count multiple times if multiple CPMs are assigned at safe level)"

                    $tempstr = ""
                    $tempstr2 = ""
                    $tempstr3 = ""
                    $tempstr4 = ""
                    $AllKeys = $OutputHash.Keys
                    $GetMinMax = @()

                    foreach($key in $AllKeys){
                        $curCount = $OutputHash.$key.counter
                        $GetMinMax += $curCount
                        $textColor =  '#{0:X6}' -f (Get-Random -Maximum 0x1000000)

                        $tempstr += "`"$key`","
                        $tempstr2 += "$curCount,"
                        $tempstr3 += "`"green`","
                        $tempstr4 += "`"$textColor`","
                    }

                    if($GetMinMax.count -ne 0){
                        $GetMinMax = $GetMinMax | Sort-Object
                        $mintick = 0
                        $maxtick = $GetMinMax[$GetMinMax.Count - 1]
                    }
                    else{
                        $mintick = 0
                        $maxtick = 0
                    }

                    if(![String]::IsNullOrEmpty($tempstr)){
                        $tempstr = $tempstr.Substring(0,$tempstr.Length-1)
                    }
                    if(![String]::IsNullOrEmpty($tempstr2)){
                        $tempstr2 = $tempstr2.Substring(0,$tempstr2.Length-1)
                    }
                    if(![String]::IsNullOrEmpty($tempstr3)){
                        $tempstr3 = $tempstr3.Substring(0,$tempstr3.Length-1)
                    }
                    if(![String]::IsNullOrEmpty($tempstr4)){
                        $tempstr4 = $tempstr4.Substring(0,$tempstr4.Length-1)
                    }

                    $htmlData += @{
                        Recommendation1 = $recommendation1
                        Recommendation2 = $recommendation2
                        Recommendation3 = $recommendation3
                        titlesplit = $titlesplit
                        outputfile = $outputfile
                        metricTag = $metricTag
                        OutputDataJSON = $OutputDataJSON
                        curTime = $curTime
                        tablestr = $tablestr
                        metricexplanation = $metricexplanation
                        HTMLChart = $HTMLChart
                        tempstr = $tempstr
                        tempstr2 = $tempstr2
                        tempstr3 = $tempstr3
                        tempstr4 = $tempstr4
                        datahash = $OutputHash
                        maxtick = $maxtick
                        mintick = $mintick
                    }
                }
            }
            if($TargetMetric -eq "CPMAccountManagementStatus"){
                $tagout = "accounts"
                #INITIALIZE DATE HASH
                $AccountsHash = @{
                    ManagedAccounts = @{
                        counter = 0
                        RawData = @()
                    }
                    NoCPMAssigned = @{
                        counter = 0
                        RawData = @()
                    }
                    NoPlatformActions = @{
                        counter = 0
                        RawData = @()
                    }
                    InactivePlatform = @{
                        counter = 0
                        RawData = @()
                    }
                    AccountDisabled = @{
                        counter = 0
                        RawData = @()
                    }
                    Unknown = @{
                        counter = 0
                        RawData = @()
                    }
                }
                $AllAccounts = Get-VPASAllAccounts
                if(!$AllAccounts){
                    Write-Verbose "FAILED TO PULL ALL ACCOUNTS"
                    Write-VPASOutput -str "FAILED TO PULL ALL ACCOUNTS" -type E
                    Write-VPASOutput -str $_ -type E
                    return $false
                }
                $SafeHash = @{}
                $PlatformHash = @{}

                $AllSafes = Get-VPASAllSafes
                if(!$AllSafes){
                    Write-Verbose "FAILED TO PULL ALL SAFES"
                    Write-VPASOutput -str "FAILED TO PULL ALL SAFES" -type E
                    Write-VPASOutput -str $_ -type E
                    return $false
                }
                foreach($safe in $AllSafes.value){
                    $recSafeName = $safe.safeName
                    if($recSafeName -ne "PasswordManager_Pending"){
                        if(!$SafeHash.$recSafeName){
                            $managingCPM = $safe.managingCPM
                            if([String]::IsNullOrEmpty($managingCPM)){
                                $managingCPM = "NONE"
                            }
                            $SafeHash += @{
                                $recSafeName = $managingCPM
                            }
                        }
                    }
                }

                $AllPlatforms = Get-VPASAllPlatforms
                if(!$AllPlatforms){
                    Write-Verbose "FAILED TO PULL ALL PLATFORMS"
                    Write-VPASOutput -str "FAILED TO PULL ALL PLATFORMS" -type E
                    Write-VPASOutput -str $_ -type E
                    return $false
                }
                foreach($plat in $AllPlatforms.Platforms){
                    $recPlatformID = $plat.general.id
                    if(!$PlatformHash.$recPlatformID){
                        $val = $plat.privilegedAccessWorkflows.enforceOnetimePasswordAccess
                        if($val){
                            $OTP = "YES"
                        }
                        else{
                            $OTP = "NO"
                        }

                        $val = $plat.general.active
                        if($val){
                            $Active = "YES"
                        }
                        else{
                            $Active = "NO"
                        }

                        $val = $plat.credentialsManagement.performPeriodicChange
                        if($val){
                            $ChangeStyle = "AUTOMATIC"
                        }
                        else{
                            $ChangeStyle = "MANUAL"
                        }

                        $val = $plat.credentialsManagement.performPeriodicVerification
                        if($val){
                            $VerifyStyle = "AUTOMATIC"
                        }
                        else{
                            $VerifyStyle = "MANUAL"
                        }

                        $PlatformHash += @{
                            $recPlatformID = @{
                                Change = $ChangeStyle
                                Verify = $VerifyStyle
                                OneTimePassword = $OTP
                                Active = $Active
                            }
                        }
                    }
                }
                $PlatformHash += @{
                    "BLANK_PLATFORM" = @{
                        Change = "MANUAL"
                        Verify = "MANUAL"
                        OneTimePassword = "NO"
                        Active = "YES"
                    }
                }

                foreach($acct in $AllAccounts.value){
                    $recPlatformID = $acct.platformId
                    $recAcctID = $acct.id
                    $recSafeName = $acct.safeName
                    $recStatus = $acct.secretManagement.automaticManagementEnabled

                    if([String]::IsNullOrEmpty($recPlatformID)){
                        $recPlatformID = "BLANK_PLATFORM"
                    }

                    #CALCULATE STATUS
                    $UnknownFlag = $false
                    $recordedEntry = $false

                    #FIRST PASS, NO CPM ON SAFE
                    if(!$recordedEntry){
                        if($SafeHash.$recSafeName){
                            if($SafeHash.$recSafeName -eq "NONE"){
                                #$AccountsHash.NoCPMAssigned += 1
                                $AccountsHash.NoCPMAssigned.counter += 1
                                $AccountsHash.NoCPMAssigned.RawData += $acct
                                $UnknownFlag = $false
                                $recordedEntry = $true
                            }
                        }
                        else{
                            $UnknownFlag = $true
                        }
                    }

                    #SECOND PASS, PLATFORM ACTIVE OR NOT
                    if(!$recordedEntry){
                        if($PlatformHash.$recPlatformID){
                            if($PlatformHash.$recPlatformID.Active -eq "NO"){
                                #$AccountsHash.InactivePlatform += 1
                                $AccountsHash.InactivePlatform.counter += 1
                                $AccountsHash.InactivePlatform.RawData += $acct
                                $UnknownFlag = $false
                                $recordedEntry = $true
                            }
                        }
                        else{
                            $UnknownFlag = $true
                        }
                    }

                    #THIRD PASS, PLATFORM ACTIONS
                    if(!$recordedEntry){
                        if($PlatformHash.$recPlatformID){
                            if($PlatformHash.$recPlatformID.Change -eq "MANUAL" -and $PlatformHash.$recPlatformID.Verify -eq "MANUAL" -and $PlatformHash.$recPlatformID.OneTimePassword -eq "NO"){
                                #$AccountsHash.NoPlatformActions += 1
                                $AccountsHash.NoPlatformActions.counter += 1
                                $AccountsHash.NoPlatformActions.RawData += $acct
                                $UnknownFlag = $false
                                $recordedEntry = $true
                            }
                        }
                        else{
                            $UnknownFlag = $true
                        }
                    }

                    #FOURTH PASS, ACCOUNT IS INACTIVE
                    if(!$recordedEntry){
                        if(!$recStatus){
                            #$AccountsHash.AccountDisabled += 1
                            $AccountsHash.AccountDisabled.counter += 1
                            $AccountsHash.AccountDisabled.RawData += $acct
                            $UnknownFlag = $false
                            $recordedEntry = $true
                        }
                    }

                    #FIFTH/SIXTH PASS, UNKNOWN/MANAGED
                    if(!$recordedEntry){
                        if($UnknownFlag){
                            #$AccountsHash.Unknown += 1
                            $AccountsHash.Unknown.counter += 1
                            $AccountsHash.Unknown.RawData += $acct
                        }
                        else{
                            #$AccountsHash.ManagedAccounts += 1
                            $AccountsHash.ManagedAccounts.counter += 1
                            $AccountsHash.ManagedAccounts.RawData += $acct
                        }
                    }
                }

                if($MetricFormat -eq "JSON" -or $MetricFormat -eq "ALL"){
                    $outputfile = "$OutputDirectory/CPMAccountManagementStatus.json"
                    $OutputDataJSON = $AccountsHash | ConvertTo-Json -Depth 100
                    Write-Output $OutputDataJSON | Set-Content $outputfile
                }
                if($MetricFormat -eq "NONE"){
                    $htmlData += @{
                        datahash = $OutputHash
                    }
                }
                if($MetricFormat -eq "HTML" -or $MetricFormat -eq "ALL"){
                    $outputfile = "$OutputDirectory/CPMAccountManagementStatus.html"
                    $titlesplit = "CPM Account Management Status"
                    $metricTag = "CPM Metrics"
                    $recommendation1 = "Verification ensures that the credentials stored in the Vault are accurate and up-to-date. This helps prevent potential issues that may arise from using outdated or incorrect credentials, such as authentication failures or access denials. Consider setting up verification for accounts that are not rotating"
                    $recommendation2 = "Configure automated password rotation policies for privileged accounts within CyberArk. By regularly rotating passwords, you can help ensure that accounts remain active and accessible while also enhancing security by reducing the risk of credential compromise."
                    $recommendation3 = "The CPM automates various management tasks related to the safe, such as password rotation, access provisioning, and policy enforcement. By assigning a CPM to a safe, you can streamline these management processes and ensure consistency in policy enforcement across your organization's CyberArk environment."
                    $OutputDataJSON = $AccountsHash | ConvertTo-Json -Depth 100
                    $curTime = get-date -Format "MM/dd/yyyy HH:mm:ss"
                    if($HTMLChart -eq "ALL"){
                        $tablestr = "Bar Graph, Line Graph, Pie Chart"
                    }
                    else{
                        $tablestr = $HTMLChart
                    }
                    $metricexplanation = "This metric tracks the current account management status of accounts in the system. The accounts do NOT double count (meaning for example an account will not be recorded as `"NoCPMAssigned`" as well as `"AccountDisabled`").<br>Order of priority goes: NoCPMAssigned > InactivePlatform > NoPlatformActions > AccountDisabled > Managed > Unknown"

                    $tempstr = ""
                    $tempstr2 = ""
                    $tempstr3 = ""
                    $tempstr4 = ""
                    $AllKeys = $AccountsHash.Keys
                    $AmtKeys = $AllKeys.Count
                    $counter = $AmtKeys
                    $GetMinMax = @()

                    foreach($key in $AllKeys){
                        $curCount = $AccountsHash.$key.counter
                        $GetMinMax += $curCount
                        $textColor =  '#{0:X6}' -f (Get-Random -Maximum 0x1000000)

                        $tempstr += "`"$key`","
                        $tempstr2 += "$curCount,"
                        $tempstr3 += "`"green`","
                        $tempstr4 += "`"$textColor`","

                    }

                    if($GetMinMax.count -ne 0){
                        $GetMinMax = $GetMinMax | Sort-Object
                        $mintick = 0
                        $maxtick = $GetMinMax[$GetMinMax.Count - 1]
                    }
                    else{
                        $mintick = 0
                        $maxtick = 0
                    }

                    if(![String]::IsNullOrEmpty($tempstr)){
                        $tempstr = $tempstr.Substring(0,$tempstr.Length-1)
                    }
                    if(![String]::IsNullOrEmpty($tempstr2)){
                        $tempstr2 = $tempstr2.Substring(0,$tempstr2.Length-1)
                    }
                    if(![String]::IsNullOrEmpty($tempstr3)){
                        $tempstr3 = $tempstr3.Substring(0,$tempstr3.Length-1)
                    }
                    if(![String]::IsNullOrEmpty($tempstr4)){
                        $tempstr4 = $tempstr4.Substring(0,$tempstr4.Length-1)
                    }

                    $htmlData += @{
                        Recommendation1 = $recommendation1
                        Recommendation2 = $recommendation2
                        Recommendation3 = $recommendation3
                        titlesplit = $titlesplit
                        outputfile = $outputfile
                        metricTag = $metricTag
                        OutputDataJSON = $OutputDataJSON
                        curTime = $curTime
                        tablestr = $tablestr
                        metricexplanation = $metricexplanation
                        HTMLChart = $HTMLChart
                        tempstr = $tempstr
                        tempstr2 = $tempstr2
                        tempstr3 = $tempstr3
                        tempstr4 = $tempstr4
                        datahash = $AccountsHash
                        maxtick = $maxtick
                        mintick = $mintick
                    }
                }

            }

            $datahash = $htmlData.datahash
            if($MetricFormat -eq "HTML" -or $MetricFormat -eq "ALL"){

                #OUTPUT DATA IN A PRETTY HTML
write-output "
<!DOCTYPE html>
<html>
<head>
<title>$TargetMetric</title>
<style>
    body {
        font-family: Arial, sans-serif;
        background-color: #c0c0c0;
        margin: 0;
        padding: 20px;
    }
    .metrics-container3 {
        background-color: #333;
        border-radius: 16px;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
        padding: 20px;
        margin: 0;
        color: white;
        font-size: 24px;
        font-weight: bold;
        Text-align: center;
    }
    .metrics-container2 {
        background-color: #333;
        border-radius: 16px;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
        padding: 20px;
        margin: 0;
        color: white;
    }
    .metrics-container {
        background-color: #fff;
        border-radius: 16px;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
        padding: 20px;
        margin: 0;
    }
    .metric {
        margin-bottom: 10px;
    }
    .metric-label {
        font-weight: bold;
    }
</style>
</head>
<script src=`"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js`"></script>
<body>
<div style=`"max-width: 1200px; width: 100%; margin: 0 auto;`">
    <div style=`"width: 95%; margin-right: 1%; margin-left: 1%`" class=`"metrics-container3`">
        $titlesplit <small><small>(Powered By Vpas)</small></small>
    </div>
    <br>
    <div style=`"display: flex; width: 99%;`">
        <div style=`"width:48%; margin-left: 1%; margin-right: 1%;`" class=`"metrics-container`">
            <div class=`"metric`">
                <span class=`"metric-label`">Generated By:</span> $AuthenticatedAs
            </div>
            <div class=`"metric`">
                <span class=`"metric-label`">Generated Date:</span> $curTime
            </div>
            <div class=`"metric`">
                <span class=`"metric-label`">Metric Category:</span> $metricTag
            </div>
            <div class=`"metric`">
                <span class=`"metric-label`">Output Table(s):</span> $tablestr
            </div>
        </div>
 
        <div style=`"width:48%; margin-left: 1%; margin-right: 1;%`" class=`"metrics-container`">
            <div class=`"metric`">
                <span class=`"metric-label`">Metric Type:</span><small> $titlesplit</small>
            </div>
            <div class=`"metric`">
                <span class=`"metric-label`">Explanation:</span> <small>$metricexplanation</small>
            </div>
        </div>
    </div>
    <br>
"
 | Set-content $outputfile

                if($HTMLChart -eq "BarGraph" -or $HTMLChart -eq "ALL"){
                    write-output "<div style=`"max-width:95%; margin-right: 1%; margin-left: 1%`"class=`"metrics-container`"><canvas height=`"500%`" id=`"myChartBAR`" style=`"width:100%;`"></canvas></div>`n<br><br>`n" | Add-Content $outputfile
                }
                if($HTMLChart -eq "LineGraph" -or $HTMLChart -eq "ALL"){
                    write-output "<div style=`"max-width:95%; margin-right: 1%; margin-left: 1%`"class=`"metrics-container`"><canvas height=`"500%`" id=`"myChartLINE`" style=`"width:100%;`"></canvas></div>`n<br><br>`n"| Add-Content $outputfile
                }
                if($HTMLChart -eq "PieChart" -or $HTMLChart -eq "ALL"){
                    write-output "<div style=`"max-width:95%; margin-right: 1%; margin-left: 1%`"class=`"metrics-container`"><canvas height=`"500%`" id=`"myChartPIE`" style=`"width:100%;`"></canvas></div>`n<br><br>`n"| Add-Content $outputfile
                }

Write-Output "
    <div style=`"display: flex;`">
        <div style=`"width:33%; margin-right: 1%; margin-left: 1%`" class=`"metrics-container`">
            <div class=`"metric`">
                <span class=`"metric-label`">Totals:</span>
            </div>
"
 | Add-Content $outputfile

                foreach($key in $datahash.Keys){
                    $curCount = $datahash."$key".counter
Write-Output "
            <div class=`"metric`">
                <span class=`"metric-label`"><small>&emsp;$key`:</small></span><small> $curCount $tagout</small>
            </div>
"
 | Add-Content $outputfile
                }

Write-Output "
        </div>
        <div style=`"max-width:58%; width: 68%; margin-right: 1%; margin-left: 1%`" class=`"metrics-container`">
            <div class=`"metric`">
                <span class=`"metric-label`">Recommendations:</span>
            </div>
            <div class=`"metric`">
                <span class=`"metric-label`"><small>&emsp;1)</small></span> <small>$recommendation1</small>
            </div>
            <div class=`"metric`">
                <span class=`"metric-label`"><small>&emsp;2)</small></span> <small>$recommendation2</small>
            </div>
            <div class=`"metric`">
                <span class=`"metric-label`"><small>&emsp;3)</small></span> <small>$recommendation3</small>
            </div>
        </div>
    </div>
    <br>
"
 | Add-Content $outputfile
if(!$HideRawData){
Write-Output "
    <div style=`"max-width:95%; width: 95%; margin-right: 1%; margin-left: 1%`" class=`"metrics-container`">
        <div class=`"metric`">
            <span class=`"metric-label`">Raw Data:</span>
            <div><button onclick=`"copyText()`">Copy JSON</button></div>
            <br>
            <div style=`"max-width:95%; width: 95%; margin-right: 1%; margin-left: 1%`" class=`"metrics-container2`">
            <span id=`"CopyText`" ><small>$OutputDataJSON</small></span>
            </div>
        </div>
    </div>
"
 | Add-Content $outputfile
}

Write-Output "
</div>
<script>
"
 | Add-Content $outputfile
                if($HTMLChart -eq "BarGraph" -or $HTMLChart -eq "ALL"){
                    write-output "const xValuesBAR = [$tempstr];" | Add-Content $outputfile
                    write-output "const yValuesBAR = [$tempstr2];" | Add-Content $outputfile
                    write-output "const barColorsBAR = [$tempstr3];" | Add-Content $outputfile
                }
                if($HTMLChart -eq "LineGraph" -or $HTMLChart -eq "ALL"){
                    write-output "const xValuesLINE = [$tempstr];" | Add-Content $outputfile
                    write-output "const yValuesLINE = [$tempstr2];" | Add-Content $outputfile
                }
                if($HTMLChart -eq "PieChart" -or $HTMLChart -eq "ALL"){
                    write-output "const xValuesPIE = [$tempstr];" | Add-Content $outputfile
                    write-output "const yValuesPIE = [$tempstr2];" | Add-Content $outputfile
                    write-output "const barColorsPIE = [$tempstr4];" | Add-Content $outputfile
                }

if($HTMLChart -eq "BarGraph" -or $HTMLChart -eq "ALL"){
#BAR
Write-Output "
new Chart(`"myChartBAR`", {
    type: `"bar`",
    data: {
        labels: xValuesBAR,
        datasets: [{
            backgroundColor: barColorsBAR,
            data: yValuesBAR
        }]
    },
    options: {
        scales: {
            yAxes: [{
                ticks: {
                    beginAtZero: true
                }
            }]
        },
        legend: {display: false},
        title: {
            display: true,
            text: `"$titlesplit`"
        }
    }
});
"
 | Add-Content $outputfile
}
if($HTMLChart -eq "LineGraph" -or $HTMLChart -eq "ALL"){
#LINE
Write-Output "
new Chart(`"myChartLINE`", {
    type: `"line`",
    data: {
        labels: xValuesLINE,
        datasets: [{
            label: `"$titlesplit`",
            fill: false,
            lineTension: 0,
            backgroundColor: `"rgba(0,0,255,1.0)`",
            borderColor: `"rgba(0,0,255,0.1)`",
            data: yValuesLINE
        }]
    },
    options: {
        legend: {display: true},
        scales: {
            yAxes: [{ticks: {min: $mintick, max:$maxtick}}],
        }
    }
});
"
 | Add-Content $outputfile
}
if($HTMLChart -eq "PieChart" -or $HTMLChart -eq "ALL"){
#PIE
write-output "
new Chart(`"myChartPIE`", {
    type: `"pie`",
    data: {
        labels: xValuesPIE,
        datasets: [{
            backgroundColor: barColorsPIE,
            data: yValuesPIE
        }]
    },
    options: {
        title: {
            display: true,
            text: `"$titlesplit`"
        }
    }
});
function copyText() {
        var copyText = document.getElementById(`"CopyText`");
        var textArea = document.createElement(`"textarea`");
        textArea.value = copyText.textContent;
        document.body.appendChild(textArea);
        textArea.select();
        document.execCommand(`"Copy`");
        textArea.remove();
        alert(`"JSON copied to clipboard`");
    }
"
 | Add-Content $outputfile
}


                write-output "</script>`n</body>`n</html>" | Add-Content $outputfile
            }
            return $datahash
        }catch{
            Write-Verbose "UNABLE TO RUN REPORT...RETURNING FALSE"
            Write-VPASOutput -str "UNABLE TO RUN REPORT...RETURNING FALSE" -type E
            Write-VPASOutput -str $_ -type E
            return $false
        }
    }
    End{
        $log = Write-VPASTextRecorder -inputval $CommandName -token $token -LogType DIVIDER
    }
}