public/Invoke-VPASMetricsAccounts.ps1

<#
.Synopsis
   RUN VARIOUS ACCOUNTS METRICS FROM CYBERARK
   CREATED BY: Vadim Melamed, EMAIL: vmelamed5@gmail.com
.DESCRIPTION
   USE THIS FUNCTION TO GENERATE VARIOUS ACCOUNT 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: OnboardedAccountTypes, AccountsOnboardedXDays, AccountComplianceStatus
.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 DayRange
   Specify the date range for the selected metric report
.PARAMETER AmtOfSets
   Specify the length of historic data to be included in the metric report
.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-VPASMetricsAccounts -TargetMetric OnboardedAccountTypes -OutputDirectory "C:\temp\VPASMetrics" -MetricFormat ALL -HTMLChart ALL
.OUTPUTS
   HashTable object if successful
   $false if failed
#>

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

        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,HelpMessage="Enter TargetMetric to be generated (OnboardedAccountTypes, AccountsOnboardedXDays, AccountComplianceStatus)",Position=0)]
        [ValidateSet('OnboardedAccountTypes','AccountsOnboardedXDays','AccountComplianceStatus')]
        [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)]
        [String]$DayRange,

        [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true,Position=5)]
        [String]$AmtOfSets,

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

        [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true,Position=7)]
        [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"
            }

            if($TargetMetric -eq "OnboardedAccountTypes"){
                $tagout = "accounts"
                #INITIALIZE HASH
                $OutputHash = @{}
                $PlatformLookup = @{}

                $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){
                    $PlatType = $plat.general.systemType
                    $PlatID = $plat.general.id

                    $PlatformLookup += @{
                        $PlatID = $PlatType
                    }

                    if(!$OutputHash.$PlatType){
                        $OutputHash += @{
                            #$PlatType = 0
                            $PlatType = @{
                                counter = 0
                                RawData = @()
                            }
                        }
                    }
                }
                $PlatformLookup += @{
                    "BLANK_PLATFORM" = "Unknown"
                }
                $OutputHash += @{
                    "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
                }
                foreach($rec in $AllAccounts.value){
                    $targetPlat = $rec.platformID
                    if([String]::IsNullOrEmpty($targetPlat)){
                        $targetPlat = "BLANK_PLATFORM"
                    }

                    $lookupKey = $PlatformLookup.$targetPlat
                    $OutputHash.$lookupKey.counter += 1
                    $OutputHash.$lookupKey.RawData += $rec
                }

                if($MetricFormat -eq "JSON" -or $MetricFormat -eq "ALL"){
                    $outputfile = "$OutputDirectory/OnboardedAccountTypes.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/OnboardedAccountTypes.html"
                    $titlesplit = "Onboarded Account Types"
                    $metricTag = "Account Metrics"
                    $recommendation1 = "Schedule regular account discovery scans to ensure that new privileged accounts are discovered and onboarded, and that existing accounts remain up-to-date. Monitor the account discovery process and review scan results for any anomalies or issues that may require attention."
                    $recommendation2 = "Expand the onboarding journey to identify different types of accounts that exist within the organization. Beyond traditional privileged accounts (such as windows or linux accounts), consider other types of accounts, such as service accounts, application accounts, and database accounts."
                    $recommendation3 = "Take the time to explore the various offerings available on the CyberArk Marketplace. Browse through the different categories, such as integrations, connectors, plugins, and extensions, to discover tools and solutions that will help to expand onboarding."
                    $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 type of accounts that have been onboarded into the system. The account type is determined by the base type of the platform that the account is attached to"

                    $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 "AccountsOnboardedXDays"){
                $tagout = "accounts"
                if([String]::IsNullOrEmpty($DayRange)){
                    Write-VPASOutput -str "NO DayRange SUPPLIED, ENTER DayRange (1 - 365): " -type Y
                    $DayRange = Read-Host
                }
                if([String]::IsNullOrEmpty($AmtOfSets)){
                    Write-VPASOutput -str "NO AmtOfSets SUPPLIED, ENTER AmtOfSets (1 - 365): " -type Y
                    $AmtOfSets = Read-Host
                }

                try{
                    $AmtDaysInt = [Int]$DayRange
                    if($AmtDaysInt -lt 1 -or $AmtDaysInt -gt 365){
                        Write-Verbose "INVALID INPUT FOR DAY RANGE: $DayRange ...MUST BE AN INTEGRET BETWEEN 1 - 365"
                        Write-VPASOutput -str "INVALID INPUT FOR DAY RANGE: $DayRange ...MUST BE AN INTEGRET BETWEEN 1 - 365" -type E
                        return $false
                    }
                }catch{
                    Write-Verbose "INVALID INPUT FOR DAY RANGE: $DayRange ...MUST BE AN INTEGER BETWEEN 1 - 365"
                    Write-VPASOutput -str "INVALID INPUT FOR DAY RANGE: $DayRange ...MUST BE AN INTEGER BETWEEN 1 - 365" -type E
                    Write-VPASOutput -str $_ -type E
                    return $false
                }

                try{
                    $AmtSetsInt = [Int]$AmtOfSets
                    if($AmtSetsInt -lt 1 -or $AmtSetsInt -gt 365){
                        Write-Verbose "INVALID INPUT FOR SET AMOUNT: $AmtOfSets...MUST AN INTEGER BETWEEN 1 - 365"
                        Write-VPASOutput -str "INVALID INPUT FOR SET AMOUNT: $AmtOfSets...MUST AN INTEGER BETWEEN 1 - 365" -type E
                        return $false
                    }
                }catch{
                    Write-Verbose "INVALID INPUT FOR SET AMOUNT: $AmtOfSets ...MUST AN INTEGER BETWEEN 1 - 365"
                    Write-VPASOutput -str "INVALID INPUT FOR SET AMOUNT: $AmtOfSets ...MUST AN INTEGER BETWEEN 1 - 365" -type E
                    Write-VPASOutput -str $_ -type E
                    return $false
                }

                #INITIALIZE DATE HASH
                $AccountHash = @{}
                $CurTime = ([int][double]::Parse((Get-Date (get-date (get-date).Date).ToLocalTime() -UFormat %s))) + 86400
                $NumSeconds = ($AmtDaysInt * 86400)

                $counter = 0
                while($counter -lt $AmtSetsInt){
                    $counter += 1
                    $MaxNum = $CurTime - 1
                    $CurTime = $CurTime - $NumSeconds
                    $MinNum = $CurTime

                    $UniqueKey = "Set" + $counter
                    $AccountHash += @{
                        $UniqueKey = @{
                            Max = $MaxNum
                            Min = $MinNum
                            Count = 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
                }
                foreach($acct in $AllAccounts.value){
                    $EpochStart = $acct.createdTime

                    $parser = $AmtSetsInt
                    while($parser -gt 0){
                        $uniqueKey = "Set$parser"
                        $setmin = $AccountHash.$uniqueKey.Min
                        $setmax = $AccountHash.$uniqueKey.Max
                        $setcount = $AccountHash.$uniqueKey.Count

                        if($EpochStart -ge $setmin -and $EpochStart -le $setmax){
                            $setcount += 1
                            $AccountHash.$uniqueKey.Count = $setcount
                            $parser = 0
                            $AccountHash.$UniqueKey.RawData += $acct
                        }
                        else{
                            $parser -= 1
                        }
                    }
                }

                #CONVERT THINGS TO HUMAN READABLE TIME
                $origin = New-Object -Type DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0
                $parser = $AmtSetsInt
                while($parser -gt 0){
                    $uniqueKey = "Set$parser"
                    $setmin = $AccountHash.$uniqueKey.Min
                    $setmax = $AccountHash.$uniqueKey.Max

                    $newmin = $origin.AddSeconds($setmin).ToShortDateString()
                    $newmax = $origin.AddSeconds($setmax).ToShortDateString()

                    $AccountHash.$uniqueKey.Min = $newmin
                    $AccountHash.$uniqueKey.Max = $newmax

                    $parser -= 1
                }

                if($MetricFormat -eq "JSON" -or $MetricFormat -eq "ALL"){
                    $outputfile = "$OutputDirectory/AccountsOnboarded" + $DayRange + "Days.json"
                    $OutputDataJSON = $AccountHash | ConvertTo-Json -Depth 100
                    Write-Output $OutputDataJSON | Set-Content $outputfile
                }
                if($MetricFormat -eq "NONE"){
                    $htmlData += @{
                        datahash = $AccountHash
                    }
                }
                if($MetricFormat -eq "HTML" -or $MetricFormat -eq "ALL"){
                    $outputfile = "$OutputDirectory/AccountsOnboarded" + $DayRange + "Days.html"
                    $titlesplit = "Accounts Onboarded In The Last $DayRange Days"
                    $metricTag = "Account Metrics"
                    $recommendation1 = "Schedule regular account discovery scans to ensure that new privileged accounts are discovered and onboarded, and that existing accounts remain up-to-date. Monitor the account discovery process and review scan results for any anomalies or issues that may require attention."
                    $recommendation2 = "Expand the onboarding journey to identify different types of accounts that exist within the organization. Beyond traditional privileged accounts (such as windows or linux accounts), consider other types of accounts, such as service accounts, application accounts, and database accounts."
                    $recommendation3 = "Utilize Cyberark's Discovery and Audit (DNA) scanning, which is a feature used to discover and audit privileged accounts and secrets across your organization's IT infrastructure. Running frequent DNA scans helps ensure that your CyberArk Vault is up-to-date with the latest information about privileged accounts and secrets."
                    $OutputDataJSON = $AccountHash | 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 how many accounts have been onboarded in the past $DayRange days, including configurable historical data"

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

                    while($counter -gt 0){
                        $key = "Set$counter"
                        $minVal = $AccountHash.$key.Min
                        $maxVal = $AccountHash.$key.Max
                        $curCount = $AccountHash.$key.count
                        $GetMinMax += $curCount
                        $textColor =  '#{0:X6}' -f (Get-Random -Maximum 0x1000000)

                        $outputstr = "$maxVal - $minVal"
                        $tempstr += "`"$outputstr`","
                        $tempstr2 += "$curCount,"
                        $tempstr3 += "`"green`","
                        $tempstr4 += "`"$textColor`","

                        $counter -= 1
                    }

                    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 = $AccountHash
                        maxtick = $maxtick
                        mintick = $mintick
                    }
                }
            }
            if($TargetMetric -eq "AccountComplianceStatus"){
                $tagout = "accounts"
                #INITIALIZE HASH
                $OutputHash = @{
                    Compliant = @{
                        counter = 0
                        RawData = @()
                    }
                    NonCompliant = @{
                        counter = 0
                        RawData = @()
                    }
                    Unknown = @{
                        counter = 0
                        RawData = @()
                    }
                }
                $PlatformLookup = @{}

                $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){
                    $PlatCompliance = $plat.credentialsManagement.requirePasswordChangeEveryXDays
                    $PlatID = $plat.general.id

                    $PlatformLookup += @{
                        $PlatID = $PlatCompliance
                    }
                }
                $PlatformLookup += @{
                    "BLANK_PLATFORM" = -1
                }

                $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
                }
                foreach($rec in $AllAccounts.value){
                    $targetPlat = $rec.platformID
                    $targetAcctID = $rec.id
                    if([String]::IsNullOrEmpty($targetPlat)){
                        $targetPlat = "BLANK_PLATFORM"
                    }

                    $complianceMax = $PlatformLookup.$targetPlat
                    $CredVersions = Get-VPASPasswordHistory -AcctID $targetAcctID
                    $tempcount = 0
                    $runloop = $true
                    while($runloop){
                        $MostRecent = $CredVersions.Versions[$tempcount].modificationDate
                        $IsTemp = $CredVersions.Versions[$tempcount].isTemporary
                        if($IsTemp){
                            $tempcount += 1
                        }
                        else{
                            $runloop = $false
                        }
                    }

                    if($complianceMax -eq -1){
                        #$OutputHash.Unknown += 1
                        $OutputHash.Unknown.counter += 1
                        $OutputHash.Unknown.RawData += $rec
                    }
                    else{
                        $CurTime = ([int][double]::Parse((Get-Date (get-date (get-date).Date).ToLocalTime() -UFormat %s))) + 86400
                        $ComplianceWindow = $CurTime - ($complianceMax * 86400)

                        if($MostRecent -lt $ComplianceWindow){
                            #$OutputHash.NonCompliant += 1
                            $OutputHash.NonCompliant.counter += 1
                            $OutputHash.NonCompliant.RawData += $rec
                        }
                        else{
                            #$OutputHash.Compliant += 1
                            $OutputHash.Compliant.counter += 1
                            $OutputHash.Compliant.RawData += $rec
                        }
                    }
                }

                if($MetricFormat -eq "JSON" -or $MetricFormat -eq "ALL"){
                    $outputfile = "$OutputDirectory/AccountComplianceStatus.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/AccountComplianceStatus.html"
                    $titlesplit = "Account Compliance Status"
                    $metricTag = "Account Metrics"
                    $recommendation1 = "Utilize CyberArk's reporting capabilities to generate constant reports based on the defined compliance criteria. Specify the parameters for the report, such as the timeframe, target safes, or specific compliance rules to be evaluated."
                    $recommendation2 = "Configure automated password rotation policies for privileged accounts within CyberArk. By regularly rotating passwords, you can help ensure that accounts remain compliant and accessible while also enhancing security by reducing the risk of credential compromise."
                    $recommendation3 = "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 compliant at minimum"
                    $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 compliance status of an account based on the platform settings that have been set. The compliance interval is configurable and set within the Master Policy"

                    $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
                    }
                }
            }

            $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

                if($TargetMetric -eq "AccountsOnboardedXDays"){
                    $countkeys = $datahash.Keys.Count
                    $i = 1
                    while($i -le $countkeys){
                        $Max = $datahash."Set$i".Max
                        $Min = $datahash."Set$i".Min
                        $curCount = $datahash."Set$i".Count
Write-Output "
            <div class=`"metric`">
                <span class=`"metric-label`"><small>&emsp;Set$i) $Max-$Min`:</small></span><small> $curCount $tagout</small>
            </div>
"
 | Add-Content $outputfile
                        $i += 1
                    }
                }
                else{
                    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
    }
}