public/Invoke-VPASMetricsPlatforms.ps1

<#
.Synopsis
   RUN VARIOUS PLATFORM METRICS FROM CYBERARK
   CREATED BY: Vadim Melamed, EMAIL: vpasmodule@gmail.com
.DESCRIPTION
   USE THIS FUNCTION TO GENERATE VARIOUS PLATFORM RELATED METRICS FROM CYBERARK
.LINK
   https://vpasmodule.com/commands/Invoke-VPASMetricsPlatforms
.NOTES
   SelfHosted: TRUE
   PrivCloudStandard: TRUE
   SharedServices: TRUE
.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: AccountsAssignedToPlatforms, AutomaticVsManualRotation, AutomaticVsManualVerification
.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 HideRawData
   Removes the RawData visual from the exported output
   Helpful when exporting to a PDF or document to remove extra not needed information
.PARAMETER IgnoreSafes
   Wildcard value that will cause a record to be ignored from the metrics if the target record SafeName matches
.PARAMETER IgnorePlatforms
   Wildcard value that will cause a record to be ignored from the metrics if the target record PlatformID matches
.PARAMETER IgnoreUsernames
   Wildcard value that will cause a record to be ignored from the metrics if the target record Username matches
.PARAMETER SafeSearchQuery
   Wildcard value that will limit the metrics to only target records that match the searchquery via safe name
.PARAMETER PlatformSearchQuery
   Wildcard value that will limit the metrics to only target records that match the searchquery via platformID
.PARAMETER UsernameSearchQuery
   Wildcard value that will limit the metrics to only target records that match the searchquery via account username
.PARAMETER InputParameters
   HashTable of values containing the parameters required to make the API call
.EXAMPLE
   $GenerateReport = Invoke-VPASMetricsPlatforms -TargetMetric AutomaticVsManualVerification -MetricFormat ALL -OutputDirectory C:\Temp\Metrics\PlatformMetrics
.EXAMPLE
   $GenerateReport = Invoke-VPASMetricsPlatforms -TargetMetric AutomaticVsManualRotation -MetricFormat ALL -OutputDirectory C:\Temp\Metrics\PlatformMetrics
.EXAMPLE
   $GenerateReport = Invoke-VPASMetricsPlatforms -TargetMetric AccountsAssignedToPlatforms -MetricFormat ALL -OutputDirectory C:\Temp\Metrics\PlatformMetrics
.EXAMPLE
   $InputParameters = @{
        TargetMetric = "AccountsAssignedToPlatforms"|"AutomaticVsManualRotation"|"AutomaticVsManualVerification"
        MetricFormat = "JSON"|"HTML"|"ALL"|"NONE"
        OutputDirectory = "C:\temp\ReportOutputs"
        HideRawData = $true|$false
        IgnoreSafes = @("VaultInternal","System")
        IgnorePlatforms = @("WinDomain","WinLocal")
        IgnoreUsernames = @("IgnoreUsername1")
        SafeSearchQuery = @("vman-","vpas-")
        PlatformSearchQuery = @("norotate","store")
        UsernameSearchQuery = @("TargetUser1","TargetUser3")
   }
   $GenerateReport = Invoke-VPASMetricsAccounts -InputParameters $InputParameters
.OUTPUTS
   HashTable object if successful
   ---
   $false if failed
#>

function Invoke-VPASMetricsPlatforms{
    [OutputType([bool])]
    [CmdletBinding(DefaultParameterSetName='Set1')]
    Param(

        [Parameter(Mandatory=$true,ParameterSetName='Set1',ValueFromPipelineByPropertyName=$true,HelpMessage="Enter TargetMetric to be generated (AccountsAssignedToPlatforms, AutomaticVsManualRotation, AutomaticVsManualVerification)")]
        [ValidateSet('AccountsAssignedToPlatforms','AutomaticVsManualRotation','AutomaticVsManualVerification')]
        [String]$TargetMetric,

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

        [Parameter(Mandatory=$false,ParameterSetName='Set1',ValueFromPipelineByPropertyName=$true)]
        [String]$OutputDirectory,

        [Parameter(Mandatory=$false,ParameterSetName='Set1',ValueFromPipelineByPropertyName=$true)]
        [switch]$HideRawData,

        [Parameter(Mandatory=$false,ParameterSetName='Set1',ValueFromPipelineByPropertyName=$true)]
        [String[]]$IgnoreSafes,

        [Parameter(Mandatory=$false,ParameterSetName='Set1',ValueFromPipelineByPropertyName=$true)]
        [String[]]$IgnorePlatforms,

        [Parameter(Mandatory=$false,ParameterSetName='Set1',ValueFromPipelineByPropertyName=$true)]
        [String[]]$IgnoreUsernames,

        [Parameter(Mandatory=$false,ParameterSetName='Set1',ValueFromPipelineByPropertyName=$true)]
        [String[]]$SafeSearchQuery,

        [Parameter(Mandatory=$false,ParameterSetName='Set1',ValueFromPipelineByPropertyName=$true)]
        [String[]]$PlatformSearchQuery,

        [Parameter(Mandatory=$false,ParameterSetName='Set1',ValueFromPipelineByPropertyName=$true)]
        [String[]]$UsernameSearchQuery,

        [Parameter(Mandatory=$true,ParameterSetName='InputParameters',ValueFromPipelineByPropertyName=$true,HelpMessage="Hashtable of parameters required to make API call, refer to get-help -examples for valid inputs")]
        [hashtable]$InputParameters,

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

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

        try{
            if($PSCmdlet.ParameterSetName -eq "InputParameters"){
                $KeyHash = @{
                    set1 = @{
                        AcceptableKeys = @("TargetMetric","MetricFormat","OutputDirectory","HideRawData","IgnoreSafes","IgnorePlatforms","IgnoreUsernames","SafeSearchQuery","PlatformSearchQuery","UsernameSearchQuery")
                        MandatoryKeys = @("TargetMetric","MetricFormat")
                    }
                }
                $CheckSet = Test-VPASHashtableKeysHelper -InputHash $InputParameters -KeyHash $KeyHash

                if(!$CheckSet){
                    $log = Write-VPASTextRecorder -inputval "FAILED TO FIND TARGET PARAMETER SET" -token $token -LogType MISC
                    Write-Verbose "FAILED TO FIND TARGET PARAMETER SET"
                    Write-VPASOutput -str "FAILED TO FIND TARGET PARAMETER SET...VIEW EXAMPLES BELOW:" -type E
                    $examples = Write-VPASExampleHelper -CommandName $CommandName
                    return $false
                }
                else{
                    foreach($key in $InputParameters.Keys){
                        Set-Variable -Name $key -Value $InputParameters.$key
                    }
                }
            }
        }catch{
            $log = Write-VPASTextRecorder -inputval $_ -token $token -LogType ERROR
            $log = Write-VPASTextRecorder -inputval "REST API COMMAND RETURNED: FALSE" -token $token -LogType MISC
            Write-Verbose "FAILED TO INVOKE METRIC"
            Write-VPASOutput -str $_ -type E
            return $false
        }

        try{
            $SkipPlatformsSTR = ""
            foreach($rec in $IgnorePlatforms){
                $SkipPlatformsSTR += "*" + $rec + "*; "
            }
            $SkipUsernameSTR = ""
            foreach($rec in $IgnoreUsernames){
                $SkipUsernameSTR += "*" + $rec + "*; "
            }
            $SkipSafeSTR = ""
            foreach($rec in $IgnoreSafes){
                $SkipSafeSTR += "*" + $rec + "*; "
            }
            $TargetSafeSTR = ""
            foreach($rec in $SafeSearchQuery){
                $TargetSafeSTR += "*" + $rec + "*; "
            }
            $TargetPlatformSTR = ""
            foreach($rec in $PlatformSearchQuery){
                $TargetPlatformSTR += "*" + $rec + "*; "
            }
            $TargetUsernameSTR = ""
            foreach($rec in $UsernameSearchQuery){
                $TargetUsernameSTR += "*" + $rec + "*; "
            }

            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 = "PieChart"
            }

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

                $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){
                    $validtargetplatform = $false
                    $validtargetsafe = $false
                    $validtargetusername = $false
                    $targetPlatformID = $acct.platformId
                    $targetSafe = $acct.safeName
                    $targetUsername = $acct.userName
                    $targetAccountName = $acct.name
                    $skipval = $false

                    if($PlatformSearchQuery.Count -eq 0){
                        $validtargetplatform = $true
                    }
                    else{
                        foreach($query in $PlatformSearchQuery){
                            if($targetPlatformID -match $query){
                                $validtargetplatform = $true
                            }
                        }
                    }

                    if($UsernameSearchQuery.Count -eq 0){
                        $validtargetusername = $true
                    }
                    else{
                        foreach($query in $UsernameSearchQuery){
                            if($targetUsername -match $query){
                                $validtargetusername = $true
                            }
                        }
                    }

                    if($SafeSearchQuery.Count -eq 0){
                        $validtargetsafe = $true
                    }
                    else{
                        foreach($query in $SafeSearchQuery){
                            if($targetSafe -match $query){
                                $validtargetsafe = $true
                            }
                        }
                    }

                    if(!$validtargetplatform -or !$validtargetsafe -or !$validtargetusername){
                        $skipval = $true
                    }

                    if(!$skipval){
                        foreach($ignoreval in $IgnoreSafes){
                            if($targetSafe -match $ignoreval){
                                Write-Verbose "SKIPPING $targetAccountName : SKIP VALUE $ignoreval FOUND IN SAFE $targetSafe"
                                $skipval = $true
                            }
                        }
                    }

                    if(!$skipval){
                        foreach($ignoreval in $IgnorePlatforms){
                            if($targetPlatformID -match $ignoreval){
                                Write-Verbose "SKIPPING $targetAccountName : SKIP VALUE $ignoreval FOUND IN PLATFORM $targetPlatformID"
                                $skipval = $true
                            }
                        }
                    }

                    if(!$skipval){
                        foreach($ignoreval in $IgnoreUsernames){
                            if($targetUsername -match $ignoreval){
                                Write-Verbose "SKIPPING $targetAccountName : SKIP VALUE $ignoreval FOUND IN USERNAME $targetUsername"
                                $skipval = $true
                            }
                        }
                    }

                    if(!$skipval){
                        if([String]::IsNullOrEmpty($targetPlatformID)){ $targetPlatformID = "BLANK_PLATFORM" }

                        if($OutputHash.$targetPlatformID){
                            $OutputHash.$targetPlatformID.counter += 1
                            $OutputHash.$targetPlatformID.RawData += $acct
                        }
                        else{
                            $OutputHash += @{
                                $targetPlatformID = @{
                                    counter = 1
                                    RawData = @($acct)
                                }
                            }
                        }
                    }
                }

                $TempPlatforms = Get-VPASAllPlatforms
                if(!$TempPlatforms){
                    Write-Verbose "FAILED TO RETRIEVE PLATFORMS"
                    Write-VPASOutput -str "FAILED TO RETRIEVE PLATFORMS" -type E
                    Write-VPASOutput -str $_ -type E
                    return $false
                }
                if($PlatformSearchQuery.count -gt 0){
                    $PlatformMatrix = @()
                    $AllPlatforms = @{
                        Platforms = @()
                        Total = 0
                    }
                    foreach($plat in $TempPlatforms.Platforms){
                        $targetplatID = $plat.general.id
                        foreach($query in $PlatformSearchQuery){
                            if($targetplatID -match $query){
                                if(!$PlatformMatrix.Contains($targetplatID)){
                                    $AllPlatforms.Platforms += $plat
                                    $AllPlatforms.Total += 1
                                    $PlatformMatrix += $targetplatID
                                }
                            }
                        }
                    }

                }
                else{
                    $AllPlatforms = $TempPlatforms
                }

                foreach($plat in $AllPlatforms.Platforms){
                    $targetplatID = $plat.general.id
                    $skipval = $false

                    if(!$skipval){
                        foreach($ignoreval in $IgnorePlatforms){
                            if($targetplatID -match $ignoreval){
                                Write-Verbose "SKIPPING $targetplatID : SKIP VALUE $ignoreval FOUND IN PLATFORM $targetplatID"
                                $skipval = $true
                            }
                        }
                    }

                    if(!$skipval){
                        if([String]::IsNullOrEmpty($targetplatID)){ $targetplatID = "BLANK_PLATFORM" }
                        if($targetplatID -ne "BLANK_PLATFORM"){
                            $curActive = $plat.general.active
                            if($curActive){
                                if(!$OutputHash.$targetplatID){
                                    $OutputHash += @{
                                        $targetplatID = @{
                                            counter = 0
                                            RawData = @()
                                        }
                                    }
                                }
                            }
                        }
                        else{
                            if(!$OutputHash.$targetplatID){
                                $OutputHash += @{
                                    $targetplatID = @{
                                        counter = 0
                                        RawData = @()
                                    }
                                }
                            }
                        }
                    }
                }


                if($MetricFormat -eq "JSON" -or $MetricFormat -eq "ALL"){
                    $outputfile = "$OutputDirectory/AccountsAssignedToPlatforms.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/AccountsAssignedToPlatforms.html"
                    $titlesplit = "Amount of Accounts Assigned to Platforms"
                    $metricTag = "Platform Metrics"
                    $recommendation1 = "A platform is a set of configurations that defines how and when the system manages credentials and accesses for a particular type of system or application. Platforms are critical for ensuring secure and efficient means of management for privileged accounts."
                    $recommendation2 = "Frequent password rotation policies are a quick win for bolstering an organization's security posture. Avoid using platforms that do not support password rotation, or password storing type platforms, unless a plugin or integration is unavailable."
                    $recommendation3 = "Platforms without any associated accounts can be disabled or deleted to reduce clutter and streamline the environment."
                    $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 accounts assigned to which platforms. This metric will include disabled/inactive platforms if they contain accounts, but will ignore disabled/inactive platforms if they do not contain any accounts"

                    $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 "AutomaticVsManualRotation"){
                $tagout = "accounts"
                #INITIALIZE HASH
                $OutputHash = @{
                    Manual = @{
                        counter = 0
                    }
                    Automatic = @{
                        counter = 0
                    }
                }

                $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){
                    $validtargetplatform = $false
                    $validtargetsafe = $false
                    $validtargetusername = $false
                    $targetPlatformID = $acct.platformId
                    $targetSafe = $acct.safeName
                    $targetUsername = $acct.userName
                    $targetAccountName = $acct.name
                    $skipval = $false

                    if($PlatformSearchQuery.Count -eq 0){
                        $validtargetplatform = $true
                    }
                    else{
                        foreach($query in $PlatformSearchQuery){
                            if($targetPlatformID -match $query){
                                $validtargetplatform = $true
                            }
                        }
                    }

                    if($UsernameSearchQuery.Count -eq 0){
                        $validtargetusername = $true
                    }
                    else{
                        foreach($query in $UsernameSearchQuery){
                            if($targetUsername -match $query){
                                $validtargetusername = $true
                            }
                        }
                    }

                    if($SafeSearchQuery.Count -eq 0){
                        $validtargetsafe = $true
                    }
                    else{
                        foreach($query in $SafeSearchQuery){
                            if($targetSafe -match $query){
                                $validtargetsafe = $true
                            }
                        }
                    }

                    if(!$validtargetplatform -or !$validtargetsafe -or !$validtargetusername){
                        $skipval = $true
                    }

                    if(!$skipval){
                        foreach($ignoreval in $IgnoreSafes){
                            if($targetSafe -match $ignoreval){
                                Write-Verbose "SKIPPING $targetAccountName : SKIP VALUE $ignoreval FOUND IN SAFE $targetSafe"
                                $skipval = $true
                            }
                        }
                    }

                    if(!$skipval){
                        foreach($ignoreval in $IgnorePlatforms){
                            if($targetPlatformID -match $ignoreval){
                                Write-Verbose "SKIPPING $targetAccountName : SKIP VALUE $ignoreval FOUND IN PLATFORM $targetPlatformID"
                                $skipval = $true
                            }
                        }
                    }

                    if(!$skipval){
                        foreach($ignoreval in $IgnoreUsernames){
                            if($targetUsername -match $ignoreval){
                                Write-Verbose "SKIPPING $targetAccountName : SKIP VALUE $ignoreval FOUND IN USERNAME $targetUsername"
                                $skipval = $true
                            }
                        }
                    }

                    if(!$skipval){
                        if([String]::IsNullOrEmpty($targetPlatformID)){ $targetPlatformID = "BLANK_PLATFORM" }

                        if($targetPlatformID -eq "BLANK_PLATFORM"){
                            if($OutputHash.Manual.$targetPlatformID){
                                $OutputHash.Manual.counter += 1
                                $OutputHash.Manual.$targetPlatformID.RawData += $acct
                            }
                            else{
                                $OutputHash.Manual += @{
                                    $targetPlatformID = @{
                                        RawData = @($acct)
                                    }
                                }
                                $OutputHash.Manual.counter += 1
                            }
                        }
                        else{
                            if($OutputHash.Manual.$targetPlatformID){
                                $OutputHash.Manual.counter += 1
                                $OutputHash.Manual.$targetPlatformID.RawData += $acct
                            }
                            elseif($OutputHash.Automatic.$targetPlatformID){
                                $OutputHash.Automatic.counter += 1
                                $OutputHash.Automatic.$targetPlatformID.RawData += $acct
                            }
                            else{
                                #GET PLATFORM DETAILS IN TERMS OF ROTATION
                                $AllPlatformDetails = Get-VPASPlatformDetails -platformID $targetPlatformID
                                $ChangeStyle = $AllPlatformDetails.Details.PerformPeriodicChange

                                if($ChangeStyle -eq "No"){
                                    if($OutputHash.Manual.$targetPlatformID){
                                        $OutputHash.Manual.counter += 1
                                        $OutputHash.Manual.$targetPlatformID.RawData += $acct
                                    }
                                    else{
                                        $OutputHash.Manual += @{
                                            $targetPlatformID = @{
                                                RawData = @($acct)
                                            }
                                        }
                                        $OutputHash.Manual.counter += 1
                                    }
                                }
                                elseif($ChangeStyle -eq "Yes"){
                                    if($OutputHash.Automatic.$targetPlatformID){
                                        $OutputHash.Automatic.counter += 1
                                        $OutputHash.Automatic.$targetPlatformID.RawData += $acct
                                    }
                                    else{
                                        $OutputHash.Automatic += @{
                                            $targetPlatformID = @{
                                                RawData = @($acct)
                                            }
                                        }
                                        $OutputHash.Automatic.counter += 1
                                    }
                                }
                            }
                        }
                    }
                }

                if($MetricFormat -eq "JSON" -or $MetricFormat -eq "ALL"){
                    $outputfile = "$OutputDirectory/AutomaticVsManualRotation.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/AutomaticVsManualRotation.html"
                    $titlesplit = "Amount of Accounts With Automatic vs Manual Password Rotation Flows"
                    $metricTag = "Platform Metrics"
                    $recommendation1 = "A platform is a set of configurations that defines how and when the system manages credentials and accesses for a particular type of system or application. Platforms are critical for ensuring secure and efficient means of management for privileged accounts."
                    $recommendation2 = "Frequent password rotation policies are a quick win for bolstering an organization's security posture. Avoid using platforms that do not support password rotation, or password storing type platforms, unless a plugin or integration is unavailable."
                    $recommendation3 = "If a platform without password rotation is required for a specific use case, consider using the automatic verification flow to ensure that the credentials in CyberArk match those on the target system."
                    $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 accounts that are configured with automatic password rotation flows verse accounts configured with only a manual password rotation flow. This configuration is based on the platform the accounts are associated with."

                    $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 "AutomaticVsManualVerification"){
                $tagout = "accounts"
                #INITIALIZE HASH
                $OutputHash = @{
                    Manual = @{
                        counter = 0
                    }
                    Automatic = @{
                        counter = 0
                    }
                }

                $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){
                    $validtargetplatform = $false
                    $validtargetsafe = $false
                    $validtargetusername = $false
                    $targetPlatformID = $acct.platformId
                    $targetSafe = $acct.safeName
                    $targetUsername = $acct.userName
                    $targetAccountName = $acct.name
                    $skipval = $false

                    if($PlatformSearchQuery.Count -eq 0){
                        $validtargetplatform = $true
                    }
                    else{
                        foreach($query in $PlatformSearchQuery){
                            if($targetPlatformID -match $query){
                                $validtargetplatform = $true
                            }
                        }
                    }

                    if($UsernameSearchQuery.Count -eq 0){
                        $validtargetusername = $true
                    }
                    else{
                        foreach($query in $UsernameSearchQuery){
                            if($targetUsername -match $query){
                                $validtargetusername = $true
                            }
                        }
                    }

                    if($SafeSearchQuery.Count -eq 0){
                        $validtargetsafe = $true
                    }
                    else{
                        foreach($query in $SafeSearchQuery){
                            if($targetSafe -match $query){
                                $validtargetsafe = $true
                            }
                        }
                    }

                    if(!$validtargetplatform -or !$validtargetsafe -or !$validtargetusername){
                        $skipval = $true
                    }

                    if(!$skipval){
                        foreach($ignoreval in $IgnoreSafes){
                            if($targetSafe -match $ignoreval){
                                Write-Verbose "SKIPPING $targetAccountName : SKIP VALUE $ignoreval FOUND IN SAFE $targetSafe"
                                $skipval = $true
                            }
                        }
                    }

                    if(!$skipval){
                        foreach($ignoreval in $IgnorePlatforms){
                            if($targetPlatformID -match $ignoreval){
                                Write-Verbose "SKIPPING $targetAccountName : SKIP VALUE $ignoreval FOUND IN PLATFORM $targetPlatformID"
                                $skipval = $true
                            }
                        }
                    }

                    if(!$skipval){
                        foreach($ignoreval in $IgnoreUsernames){
                            if($targetUsername -match $ignoreval){
                                Write-Verbose "SKIPPING $targetAccountName : SKIP VALUE $ignoreval FOUND IN USERNAME $targetUsername"
                                $skipval = $true
                            }
                        }
                    }

                    if(!$skipval){
                        if([String]::IsNullOrEmpty($targetPlatformID)){ $targetPlatformID = "BLANK_PLATFORM" }

                        if($targetPlatformID -eq "BLANK_PLATFORM"){
                            if($OutputHash.Manual.$targetPlatformID){
                                $OutputHash.Manual.counter += 1
                                $OutputHash.Manual.$targetPlatformID.RawData += $acct
                            }
                            else{
                                $OutputHash.Manual += @{
                                    $targetPlatformID = @{
                                        RawData = @($acct)
                                    }
                                }
                                $OutputHash.Manual.counter += 1
                            }
                        }
                        else{
                            if($OutputHash.Manual.$targetPlatformID){
                                $OutputHash.Manual.counter += 1
                                $OutputHash.Manual.$targetPlatformID.RawData += $acct
                            }
                            elseif($OutputHash.Automatic.$targetPlatformID){
                                $OutputHash.Automatic.counter += 1
                                $OutputHash.Automatic.$targetPlatformID.RawData += $acct
                            }
                            else{
                                #GET PLATFORM DETAILS IN TERMS OF ROTATION
                                $AllPlatformDetails = Get-VPASPlatformDetails -platformID $targetPlatformID
                                $VerifyStyle = $AllPlatformDetails.Details.VFPerformPeriodicVerification

                                if($VerifyStyle -eq "No"){
                                    if($OutputHash.Manual.$targetPlatformID){
                                        $OutputHash.Manual.counter += 1
                                        $OutputHash.Manual.$targetPlatformID.RawData += $acct
                                    }
                                    else{
                                        $OutputHash.Manual += @{
                                            $targetPlatformID = @{
                                                RawData = @($acct)
                                            }
                                        }
                                        $OutputHash.Manual.counter += 1
                                    }
                                }
                                elseif($VerifyStyle -eq "Yes"){
                                    if($OutputHash.Automatic.$targetPlatformID){
                                        $OutputHash.Automatic.counter += 1
                                        $OutputHash.Automatic.$targetPlatformID.RawData += $acct
                                    }
                                    else{
                                        $OutputHash.Automatic += @{
                                            $targetPlatformID = @{
                                                RawData = @($acct)
                                            }
                                        }
                                        $OutputHash.Automatic.counter += 1
                                    }
                                }
                            }
                        }
                    }
                }

                if($MetricFormat -eq "JSON" -or $MetricFormat -eq "ALL"){
                    $outputfile = "$OutputDirectory/AutomaticVsManualVerification.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/AutomaticVsManualVerification.html"
                    $titlesplit = "Amount of Accounts With Automatic vs Manual Password Verification Flows"
                    $metricTag = "Platform Metrics"
                    $recommendation1 = "A platform is a set of configurations that defines how and when the system manages credentials and accesses for a particular type of system or application. Platforms are critical for ensuring secure and efficient means of management for privileged accounts."
                    $recommendation2 = "Verification is a process that ensures the credentials stored in CyberArk match those on the target system. This process is crucial for maintaining the integrity and accuracy of the target credential."
                    $recommendation3 = "If a platform without password rotation is required for a specific use case, consider using the automatic verification flow to ensure that the credentials in CyberArk match those on the target system."
                    $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 accounts that are configured with automatic password verification flows verse accounts configured with only a manual password verification flow. This configuration is based on the platform the accounts are associated with."

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

            #ADDING FIX TO LIMIT OUTPUT LABEL QUANTITY...top10
            $datalimit = 9
            $newtempstrsplit = $tempstr -split ","
            $newtempstr2split = $tempstr2 -split ","
            $newtempstr4split = $tempstr4 -split ","

            if($newtempstrsplit.Count -gt $datalimit){
                $stiched = for($i = 0; $i -lt $newtempstrsplit.Count; $i++){
                    [PSCustomObject]@{
                        Label = $newtempstrsplit[$i]
                        Value = [int]$newtempstr2split[$i]
                        Color = $newtempstr4split[$i]
                    }
                }
                $topValues = $stiched | Sort-Object -Property Value -Descending | Select-Object -First ($newtempstrsplit.Count)

                $tempstr = ""
                $tempstr2 = ""
                $tempstr4 = ""
                $tempcount = 0
                for($i = 0; $i -lt $topValues.Count; $i++){
                    $splitval = $topValues[$i].Value
                    $splitlabel = $topValues[$i].Label
                    $splitcolor = $topValues[$i].Color

                    if($i -lt $datalimit){
                        $tempstr += "$splitlabel,"
                        $tempstr2 += "$splitval,"
                        $tempstr4 += "$splitcolor,"
                    }
                    else{
                        $tempcount += $splitval
                    }
                }
                $lastcolor = $topValues[$datalimit].Color

                $tempstr += "`"Other`","
                $tempstr2 += "$tempcount,"
                $tempstr4 += "$lastcolor,"

                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($tempstr4)){
                    $tempstr4 = $tempstr4.Substring(0,$tempstr4.Length-1)
                }
            }



            $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;
    }
    button {
        padding: 8px 12px;
        background-color: #444;
        color: white;
        border: none;
        border-radius: 8px;
        cursor: pointer;
        font-weight: bold;
    }
    button:hover {
        background-color: #666;
    }
    canvas {
        max-width: 100%;
        height: auto;
    }
    #totalsTable tbody tr:nth-child(even) {
        background-color: #f2f2f2;
    }
    #totalsTable tbody tr:nth-child(odd) {
        background-color: #ffffff;
    }
    .dt-row-dark {
        background-color: #2c2c2c !important;
        color: #e0e0e0 !important;
    }
    .dt-row-light {
        background-color: #1a1a1a !important;
        color: #e0e0e0 !important;
    }
    .dataTables_filter input {
        background-color: #ffffff;
        color: #000000;
        border: 1px solid #ccc;
        border-radius: 6px;
        padding: 5px 8px;
    }
    body.dark-mode .dataTables_filter input {
        background-color: #2c2c2c;
        color: #e0e0e0;
        border: 1px solid #555;
    }
    .dataTables_filter input::placeholder {
        color: #999;
    }
    body.dark-mode .dataTables_filter input::placeholder {
        color: #bbb;
    }
    body.dark-mode .dataTables_filter label {
        color: white;
    }
</style>
</head>
<link rel=`"stylesheet`" type=`"text/css`" href=`"https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css`">
<script src=`"https://code.jquery.com/jquery-3.7.0.min.js`"></script>
<script src=`"https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js`"></script>
<script src=`"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js`"></script>
<script src=`"https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@0.7.0`"></script>
<body>
<div style=`"max-width: 1200px; width: 100%; margin: 0 auto;`">
    <div style=`"width: 95%; margin-right: 1%; margin-left: 1%; display: flex; justify-content: space-between; align-items: center;`" class=`"metrics-container3`">
        <div style=`"margin-left: 20%;`">$titlesplit <small><small>(Powered By Vpas)</small></small></div>
        <button onclick=`"toggleDarkMode()`" id=`"darkModeBtn`" style=`"padding: 8px 12px; background-color: #444; color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: bold;`">Dark Mode</button>
    </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>
"
 | Set-content $outputfile
                if(![String]::IsNullOrEmpty($TargetSafeSTR)){
write-output "
            <div class=`"metric`">
                <span class=`"metric-label`">Target Safe(s):</span> $TargetSafeSTR
            </div>
"
 | Add-content $outputfile
                }
                if(![String]::IsNullOrEmpty($TargetPlatformSTR)){
write-output "
            <div class=`"metric`">
                <span class=`"metric-label`">Target Platform(s):</span> $TargetPlatformSTR
            </div>
"
 | Add-content $outputfile
                }
                if(![String]::IsNullOrEmpty($TargetUsernameSTR)){
write-output "
            <div class=`"metric`">
                <span class=`"metric-label`">Target Username(s):</span> $TargetUsernameSTR
            </div>
"
 | Add-content $outputfile
                }
                if(![String]::IsNullOrEmpty($SkipPlatformsSTR)){
write-output "
            <div class=`"metric`">
                <span class=`"metric-label`">Ignore Platform(s):</span> $SkipPlatformsSTR
            </div>
"
 | Add-content $outputfile
                }
                if(![String]::IsNullOrEmpty($SkipUsernameSTR)){
write-output "
            <div class=`"metric`">
                <span class=`"metric-label`">Ignore Username(s):</span> $SkipUsernameSTR
            </div>
"
 | Add-content $outputfile
                }
                if(![String]::IsNullOrEmpty($SkipSafeSTR)){
write-output "
            <div class=`"metric`">
                <span class=`"metric-label`">Ignore Safe(s):</span> $SkipSafeSTR
            </div>
"
 | Add-content $outputfile
                }

write-output "
        </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>
 
    <div style=`"display: flex; align-items: flex-start; justify-content: space-between; max-width: 95%; margin: 0 auto;`" class=`"metrics-container`">
        <div style=`"width: 10%;`">
            <button onclick=`"toggleLabels()`" style=`"width: 100%; height: 40px;`">Toggle Labels</button>
            <button onclick=`"regenerateColors()`" style=`"width: 100%; height: 40px; margin-top: 10px;`">Randomize</button>
          </div>
      <!-- Chart container -->
      <div style=`"flex: 1;`">
        <canvas id=`"myChartPIE`" width=`"400`" height=`"400`"></canvas>
      </div>
 
      <div style=`"display: flex; flex-direction: column; gap: 10px; margin-left: 20px;`">
        <button onclick=`"setChartType('pie')`">Pie</button>
        <button onclick=`"setChartType('doughnut')`">Doughnut</button>
        <button onclick=`"setChartType('line')`">Line</button>
        <button onclick=`"setChartType('bar')`">Bar</button>
      </div>
    </div>
    <br><br>
"
 | Add-content $outputfile


Write-Output "
        <div style=`"max-width: 95%; margin-left: 0.75%; margin-top: -1%; margin-right: 0.75%`" class=`"metrics-container`">
            <span class=`"metric-label`">Totals:</span>
            <div id=`"totalsTableWrapper`" style=`"margin-left: 10%; width: 80%; max-height: 300px; overflow-y: auto; transition: max-height 0.5s ease; color:black;`">
                <table id=`"totalsTable`" style=`"width: 100%;`">
                <thead>
                    <tr>
                        <th>Value</th>
                        <th>Count</th>
                     </tr>
                </thead>
                <tbody>
"
 | 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
                        $curCount = $datahash."Set$i".Count
Write-Output " <tr><td>Set$i) $Max-$Min</td><td>$curCount</td></tr>" | Add-Content $outputfile
                        $i += 1
                    }
                }
                else{
                    foreach($key in $datahash.Keys){
                        $curCount = $datahash."$key".counter

Write-Output " <tr><td>$key</td><td>$curCount</td></tr>" | Add-Content $outputfile
                    }
                }

Write-Output "
                </tbody>
            </table>
            </div>
                <div style=`"text-align: right; margin-top: 10px;`">
                    <button id=`"toggleButton`" onclick=`"toggleTable()`">Expand</button>
                </div>
            </div>
        <div style=`"max-width: 95%; margin-left: 0.75%; margin-top: 2%; margin-right: 0.75%`" 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>
    <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>
    const xValues = [$tempstr];
    const yValues = [$tempstr2];
    const colors = [$tempstr4];
 
    let currentChart;
    let labelsVisible = true;
    let chartInstance;
    let currentType = `"pie`";
    let darkMode = false;
 
    function renderChart(type) {
        if (chartInstance) {
            chartInstance.destroy();
        }
        const chartFontColor = darkMode ? '#ffffff' : '#333333';
 
        chartInstance = new Chart(`"myChartPIE`", {
            type: type,
            data: {
                labels: xValues,
                datasets: [{
                    backgroundColor: type === 'line' || type === 'bar' ? colors[0] : colors,
                    borderColor: type === 'line' ? (darkMode ? '#ffffff' : '#000000') : '#ffffff',
                    borderWidth: 2,
                    hoverBorderColor: `"#000`",
                    hoverBorderWidth: 2,
                    data: yValues,
                    fill: false
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                layout: {
                    padding: {
                        top: 10,
                        bottom: 10
                    }
                },
                title: {
                    display: true,
                    text: `"$titlesplit`",
                    fontSize: 18,
                    fontStyle: `"bold`",
                    fontColor: chartFontColor,
                    padding: 20
                },
                legend: {
                    display: !(type === 'bar' || type === 'line'),
                    position: 'bottom',
                    labels: {
                        fontColor: chartFontColor,
                        fontSize: 14,
                        padding: 20
                    }
                },
                plugins: {
                    datalabels: {
                        display: labelsVisible,
                        color: 'white',
                        backgroundColor: 'rgba(0, 0, 0, 0.7)',
                        borderRadius: 6,
                        padding: {
                            top: 6,
                            bottom: 6,
                            left: 10,
                            right: 10
                        },
                        font: {
                            weight: 'bold',
                            size: 13
                        },
                        formatter: function(value, context) {
                            return context.chart.data.labels[context.dataIndex] + ': ' + value;
                        }
                    }
                },
                tooltips: {
                    backgroundColor: '#000',
                    titleFontSize: 14,
                    bodyFontSize: 13,
                    cornerRadius: 4,
                    caretSize: 6,
                    xPadding: 12,
                    yPadding: 8
                },
                scales: (type === 'line' || type === 'bar') ? {
                    yAxes: [{
                        ticks: { beginAtZero: true, fontColor: chartFontColor },
                        gridLines: {
                            color: '#ccc'
                        }
                    }],
                    xAxes: [{
                        ticks: { fontColor: chartFontColor },
                        gridLines: {
                            color: '#ccc'
                        }
                    }]
                } : {}
            },
            plugins: [ChartDataLabels]
        });
    }
 
    function setChartType(type) {
        currentType = type;
        renderChart(currentType);
    }
 
    function toggleLabels() {
        labelsVisible = !labelsVisible;
        renderChart(currentType);
    }
 
    // Initialize with pie chart
    Chart.defaults.global.maintainAspectRatio = false;
    renderChart(currentType);
 
    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`");
    }
 
    function getRandomColor() {
        // Generate a random color in RGB format
        const r = Math.floor(Math.random() * 256);
        const g = Math.floor(Math.random() * 256);
        const b = Math.floor(Math.random() * 256);
        return { r, g, b, hex: rgbToHex(r, g, b) };
    }
 
    function rgbToHex(r, g, b) {
        return `"#`" + [r, g, b].map(x => x.toString(16).padStart(2, '0')).join('');
    }
 
    function colorDistance(c1, c2) {
        // Euclidean distance in RGB space
        return Math.sqrt(
            Math.pow(c1.r - c2.r, 2) +
            Math.pow(c1.g - c2.g, 2) +
            Math.pow(c1.b - c2.b, 2)
        );
    }
 
    function regenerateColors() {
        const minDistance = 100; // Adjust for stricter color difference
        const newColors = [];
 
        while (newColors.length < xValues.length) {
            let newColor = getRandomColor();
            let isDistinct = newColors.every(existing => colorDistance(existing, newColor) >= minDistance);
 
            if (isDistinct) {
                newColors.push(newColor);
            }
        }
 
        for (let i = 0; i < newColors.length; i++) {
            colors[i] = newColors[i].hex;
        }
 
        renderChart(currentType);
    }
 
    `$(document).ready(function() {
        `$('#totalsTable').DataTable({
            paging: false,
            searching: true,
            info: false,
            order: [[1, 'desc']],
            columnDefs: [
                { targets: 1, className: 'dt-body-right' }
            ]
        });
    });
 
    let expanded = false;
 
    function toggleTable() {
        const wrapper = document.getElementById('totalsTableWrapper');
        const button = document.getElementById('toggleButton');
 
        if (!expanded) {
            wrapper.style.maxHeight = `"none`";
            wrapper.style.overflowY = `"visible`";
            button.textContent = `"Collapse`";
        } else {
            wrapper.style.maxHeight = `"300px`";
            wrapper.style.overflowY = `"auto`";
            button.textContent = `"Expand`";
        }
        expanded = !expanded;
    }
 
 
    function toggleDarkMode() {
        darkMode = !darkMode;
 
        document.body.classList.toggle(`"dark-mode`", darkMode);
 
        if (darkMode) {
            document.body.style.backgroundColor = `"#121212`";
            document.body.style.color = `"#e0e0e0`";
 
            document.querySelectorAll('.metrics-container, .metrics-container2, .metrics-container3, #totalsTable thead th').forEach(el => {
                el.style.backgroundColor = `"#1e1e1e`";
                el.style.color = `"#e0e0e0`";
            });
 
            document.getElementById('totalsTableWrapper').style.backgroundColor = `"#1e1e1e`";
 
            `$('#totalsTable').DataTable().destroy();
            `$('#totalsTable').DataTable({
                paging: false,
                searching: true,
                info: false,
                order: [[1, 'desc']],
                columnDefs: [
                    { targets: 1, className: 'dt-body-right' }
                ],
                `"stripeClasses`": ['dt-row-dark', 'dt-row-light']
            });
 
            Chart.defaults.global.defaultFontColor = '#e0e0e0';
            renderChart(currentType);
 
            document.getElementById('darkModeBtn').innerText = `"Light Mode`";
        } else {
            document.body.style.backgroundColor = `"#c0c0c0`";
            document.body.style.color = `"#000`";
 
            document.querySelectorAll('.metrics-container, .metrics-container2, .metrics-container3, #totalsTable thead th').forEach(el => {
                el.style.backgroundColor = `"#fff`";
                el.style.color = `"#000`";
            });
 
            document.getElementById('totalsTableWrapper').style.backgroundColor = `"#fff`";
 
            `$('#totalsTable').DataTable().destroy();
            `$('#totalsTable').DataTable({
                paging: false,
                searching: true,
                info: false,
                order: [[1, 'desc']],
                columnDefs: [
                    { targets: 1, className: 'dt-body-right' }
                ],
                `"stripeClasses`": []
            });
 
            Chart.defaults.global.defaultFontColor = '#333';
            renderChart(currentType);
 
            document.getElementById('darkModeBtn').innerText = `"Dark Mode`";
        }
    }
 
"
 | 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
    }
}

# SIG # Begin signature block
# MIIrpgYJKoZIhvcNAQcCoIIrlzCCK5MCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUb5MloYEFV8HIyEYXw/Doxdoh
# /wuggiTgMIIFbzCCBFegAwIBAgIQSPyTtGBVlI02p8mKidaUFjANBgkqhkiG9w0B
# AQwFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVy
# MRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEh
# MB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTIxMDUyNTAwMDAw
# MFoXDTI4MTIzMTIzNTk1OVowVjELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3Rp
# Z28gTGltaXRlZDEtMCsGA1UEAxMkU2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5n
# IFJvb3QgUjQ2MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjeeUEiIE
# JHQu/xYjApKKtq42haxH1CORKz7cfeIxoFFvrISR41KKteKW3tCHYySJiv/vEpM7
# fbu2ir29BX8nm2tl06UMabG8STma8W1uquSggyfamg0rUOlLW7O4ZDakfko9qXGr
# YbNzszwLDO/bM1flvjQ345cbXf0fEj2CA3bm+z9m0pQxafptszSswXp43JJQ8mTH
# qi0Eq8Nq6uAvp6fcbtfo/9ohq0C/ue4NnsbZnpnvxt4fqQx2sycgoda6/YDnAdLv
# 64IplXCN/7sVz/7RDzaiLk8ykHRGa0c1E3cFM09jLrgt4b9lpwRrGNhx+swI8m2J
# mRCxrds+LOSqGLDGBwF1Z95t6WNjHjZ/aYm+qkU+blpfj6Fby50whjDoA7NAxg0P
# OM1nqFOI+rgwZfpvx+cdsYN0aT6sxGg7seZnM5q2COCABUhA7vaCZEao9XOwBpXy
# bGWfv1VbHJxXGsd4RnxwqpQbghesh+m2yQ6BHEDWFhcp/FycGCvqRfXvvdVnTyhe
# Be6QTHrnxvTQ/PrNPjJGEyA2igTqt6oHRpwNkzoJZplYXCmjuQymMDg80EY2NXyc
# uu7D1fkKdvp+BRtAypI16dV60bV/AK6pkKrFfwGcELEW/MxuGNxvYv6mUKe4e7id
# FT/+IAx1yCJaE5UZkADpGtXChvHjjuxf9OUCAwEAAaOCARIwggEOMB8GA1UdIwQY
# MBaAFKARCiM+lvEH7OKvKe+CpX/QMKS0MB0GA1UdDgQWBBQy65Ka/zWWSC8oQEJw
# IDaRXBeF5jAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zATBgNVHSUE
# DDAKBggrBgEFBQcDAzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEMGA1Ud
# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0FBQUNlcnRpZmlj
# YXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0
# cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBDAUAA4IBAQASv6Hvi3Sa
# mES4aUa1qyQKDKSKZ7g6gb9Fin1SB6iNH04hhTmja14tIIa/ELiueTtTzbT72ES+
# BtlcY2fUQBaHRIZyKtYyFfUSg8L54V0RQGf2QidyxSPiAjgaTCDi2wH3zUZPJqJ8
# ZsBRNraJAlTH/Fj7bADu/pimLpWhDFMpH2/YGaZPnvesCepdgsaLr4CnvYFIUoQx
# 2jLsFeSmTD1sOXPUC4U5IOCFGmjhp0g4qdE2JXfBjRkWxYhMZn0vY86Y6GnfrDyo
# XZ3JHFuu2PMvdM+4fvbXg50RlmKarkUT2n/cR/vfw1Kf5gZV6Z2M8jpiUbzsJA8p
# 1FiAhORFe1rYMIIGFDCCA/ygAwIBAgIQeiOu2lNplg+RyD5c9MfjPzANBgkqhkiG
# 9w0BAQwFADBXMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVk
# MS4wLAYDVQQDEyVTZWN0aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5nIFJvb3QgUjQ2
# MB4XDTIxMDMyMjAwMDAwMFoXDTM2MDMyMTIzNTk1OVowVTELMAkGA1UEBhMCR0Ix
# GDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEsMCoGA1UEAxMjU2VjdGlnbyBQdWJs
# aWMgVGltZSBTdGFtcGluZyBDQSBSMzYwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw
# ggGKAoIBgQDNmNhDQatugivs9jN+JjTkiYzT7yISgFQ+7yavjA6Bg+OiIjPm/N/t
# 3nC7wYUrUlY3mFyI32t2o6Ft3EtxJXCc5MmZQZ8AxCbh5c6WzeJDB9qkQVa46xiY
# Epc81KnBkAWgsaXnLURoYZzksHIzzCNxtIXnb9njZholGw9djnjkTdAA83abEOHQ
# 4ujOGIaBhPXG2NdV8TNgFWZ9BojlAvflxNMCOwkCnzlH4oCw5+4v1nssWeN1y4+R
# laOywwRMUi54fr2vFsU5QPrgb6tSjvEUh1EC4M29YGy/SIYM8ZpHadmVjbi3Pl8h
# JiTWw9jiCKv31pcAaeijS9fc6R7DgyyLIGflmdQMwrNRxCulVq8ZpysiSYNi79tw
# 5RHWZUEhnRfs/hsp/fwkXsynu1jcsUX+HuG8FLa2BNheUPtOcgw+vHJcJ8HnJCrc
# UWhdFczf8O+pDiyGhVYX+bDDP3GhGS7TmKmGnbZ9N+MpEhWmbiAVPbgkqykSkzyY
# Vr15OApZYK8CAwEAAaOCAVwwggFYMB8GA1UdIwQYMBaAFPZ3at0//QET/xahbIIC
# L9AKPRQlMB0GA1UdDgQWBBRfWO1MMXqiYUKNUoC6s2GXGaIymzAOBgNVHQ8BAf8E
# BAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNVHSUEDDAKBggrBgEFBQcDCDAR
# BgNVHSAECjAIMAYGBFUdIAAwTAYDVR0fBEUwQzBBoD+gPYY7aHR0cDovL2NybC5z
# ZWN0aWdvLmNvbS9TZWN0aWdvUHVibGljVGltZVN0YW1waW5nUm9vdFI0Ni5jcmww
# fAYIKwYBBQUHAQEEcDBuMEcGCCsGAQUFBzAChjtodHRwOi8vY3J0LnNlY3RpZ28u
# Y29tL1NlY3RpZ29QdWJsaWNUaW1lU3RhbXBpbmdSb290UjQ2LnA3YzAjBggrBgEF
# BQcwAYYXaHR0cDovL29jc3Auc2VjdGlnby5jb20wDQYJKoZIhvcNAQEMBQADggIB
# ABLXeyCtDjVYDJ6BHSVY/UwtZ3Svx2ImIfZVVGnGoUaGdltoX4hDskBMZx5NY5L6
# SCcwDMZhHOmbyMhyOVJDwm1yrKYqGDHWzpwVkFJ+996jKKAXyIIaUf5JVKjccev3
# w16mNIUlNTkpJEor7edVJZiRJVCAmWAaHcw9zP0hY3gj+fWp8MbOocI9Zn78xvm9
# XKGBp6rEs9sEiq/pwzvg2/KjXE2yWUQIkms6+yslCRqNXPjEnBnxuUB1fm6bPAV+
# Tsr/Qrd+mOCJemo06ldon4pJFbQd0TQVIMLv5koklInHvyaf6vATJP4DfPtKzSBP
# kKlOtyaFTAjD2Nu+di5hErEVVaMqSVbfPzd6kNXOhYm23EWm6N2s2ZHCHVhlUgHa
# C4ACMRCgXjYfQEDtYEK54dUwPJXV7icz0rgCzs9VI29DwsjVZFpO4ZIVR33LwXyP
# DbYFkLqYmgHjR3tKVkhh9qKV2WCmBuC27pIOx6TYvyqiYbntinmpOqh/QPAnhDge
# xKG9GX/n1PggkGi9HCapZp8fRwg8RftwS21Ln61euBG0yONM6noD2XQPrFwpm3Gc
# uqJMf0o8LLrFkSLRQNwxPDDkWXhW+gZswbaiie5fd/W2ygcto78XCSPfFWveUOSZ
# 5SqK95tBO8aTHmEa4lpJVD7HrTEn9jb1EGvxOb1cnn0CMIIGGjCCBAKgAwIBAgIQ
# Yh1tDFIBnjuQeRUgiSEcCjANBgkqhkiG9w0BAQwFADBWMQswCQYDVQQGEwJHQjEY
# MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdvIFB1Ymxp
# YyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwHhcNMjEwMzIyMDAwMDAwWhcNMzYwMzIx
# MjM1OTU5WjBUMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVk
# MSswKQYDVQQDEyJTZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgQ0EgUjM2MIIB
# ojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAmyudU/o1P45gBkNqwM/1f/bI
# U1MYyM7TbH78WAeVF3llMwsRHgBGRmxDeEDIArCS2VCoVk4Y/8j6stIkmYV5Gej4
# NgNjVQ4BYoDjGMwdjioXan1hlaGFt4Wk9vT0k2oWJMJjL9G//N523hAm4jF4UjrW
# 2pvv9+hdPX8tbbAfI3v0VdJiJPFy/7XwiunD7mBxNtecM6ytIdUlh08T2z7mJEXZ
# D9OWcJkZk5wDuf2q52PN43jc4T9OkoXZ0arWZVeffvMr/iiIROSCzKoDmWABDRzV
# /UiQ5vqsaeFaqQdzFf4ed8peNWh1OaZXnYvZQgWx/SXiJDRSAolRzZEZquE6cbcH
# 747FHncs/Kzcn0Ccv2jrOW+LPmnOyB+tAfiWu01TPhCr9VrkxsHC5qFNxaThTG5j
# 4/Kc+ODD2dX/fmBECELcvzUHf9shoFvrn35XGf2RPaNTO2uSZ6n9otv7jElspkfK
# 9qEATHZcodp+R4q2OIypxR//YEb3fkDn3UayWW9bAgMBAAGjggFkMIIBYDAfBgNV
# HSMEGDAWgBQy65Ka/zWWSC8oQEJwIDaRXBeF5jAdBgNVHQ4EFgQUDyrLIIcouOxv
# SK4rVKYpqhekzQwwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAw
# EwYDVR0lBAwwCgYIKwYBBQUHAwMwGwYDVR0gBBQwEjAGBgRVHSAAMAgGBmeBDAEE
# ATBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsLnNlY3RpZ28uY29tL1NlY3Rp
# Z29QdWJsaWNDb2RlU2lnbmluZ1Jvb3RSNDYuY3JsMHsGCCsGAQUFBwEBBG8wbTBG
# BggrBgEFBQcwAoY6aHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGlj
# Q29kZVNpZ25pbmdSb290UjQ2LnA3YzAjBggrBgEFBQcwAYYXaHR0cDovL29jc3Au
# c2VjdGlnby5jb20wDQYJKoZIhvcNAQEMBQADggIBAAb/guF3YzZue6EVIJsT/wT+
# mHVEYcNWlXHRkT+FoetAQLHI1uBy/YXKZDk8+Y1LoNqHrp22AKMGxQtgCivnDHFy
# AQ9GXTmlk7MjcgQbDCx6mn7yIawsppWkvfPkKaAQsiqaT9DnMWBHVNIabGqgQSGT
# rQWo43MOfsPynhbz2Hyxf5XWKZpRvr3dMapandPfYgoZ8iDL2OR3sYztgJrbG6VZ
# 9DoTXFm1g0Rf97Aaen1l4c+w3DC+IkwFkvjFV3jS49ZSc4lShKK6BrPTJYs4NG1D
# GzmpToTnwoqZ8fAmi2XlZnuchC4NPSZaPATHvNIzt+z1PHo35D/f7j2pO1S8BCys
# QDHCbM5Mnomnq5aYcKCsdbh0czchOm8bkinLrYrKpii+Tk7pwL7TjRKLXkomm5D1
# Umds++pip8wH2cQpf93at3VDcOK4N7EwoIJB0kak6pSzEu4I64U6gZs7tS/dGNSl
# jf2OSSnRr7KWzq03zl8l75jy+hOds9TWSenLbjBQUGR96cFr6lEUfAIEHVC1L68Y
# 1GGxx4/eRI82ut83axHMViw1+sVpbPxg51Tbnio1lB93079WPFnYaOvfGAA0e0zc
# fF/M9gXr+korwQTh2Prqooq2bYNMvUoUKD85gnJ+t0smrWrb8dee2CvYZXD5laGt
# aAxOfy/VKNmwuWuAh9kcMIIGRzCCBK+gAwIBAgIQacs5SDkvNuif0aEmZmr03jAN
# BgkqhkiG9w0BAQwFADBUMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBM
# aW1pdGVkMSswKQYDVQQDEyJTZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgQ0Eg
# UjM2MB4XDTI1MDEyOTAwMDAwMFoXDTI4MDEyOTIzNTk1OVowXjELMAkGA1UEBhMC
# VVMxEzARBgNVBAgMCk5ldyBKZXJzZXkxHDAaBgNVBAoME0N5YmVyTWVsIENvbnN1
# bHRpbmcxHDAaBgNVBAMME0N5YmVyTWVsIENvbnN1bHRpbmcwggIiMA0GCSqGSIb3
# DQEBAQUAA4ICDwAwggIKAoICAQDBQmSvdfamF8o0CJr4vbHCcJ4rwx6T1HR3d32u
# 4aIf9v9p/GV4nFdG4PP9SMjWw7Nx9CLFqGPpkw7aDU2IxwpfPYExDzkCj2pgiyeV
# KlL0itTlPocb6i1cZLe/WHV7aUkGkVlfvyYIqdJ9uw711dhNWmMhlqo+/qyp+gpK
# qaiFHm6mWNVg2KLTH5Pu38cBoGhS1tn7mlQbtALNjehkpFw2AAntEIBzM3ZEg9WB
# xQlgYY0yAPkydYbJfTEOEFJqHUPTSV46jx22Jb9dl0cEIPsGrCp+Jo5Ugusp9oZE
# CZ8bGt7Vc9jYoIWGpqcRDq1JZFNCSVvNE4N3ECGjq6W3kYW7ot0CP1DkpJ93a5wr
# ksQ6bvYGUy3lghkMvzjkkq/NVUDEVcdNR7PsUFf654vSw+iLINZ+9kYg+Znplfnd
# T/JSMJDAaWkM5oLu6+ao0774QWrsHOttz7M8EDU+3PntYHglwWoej6qXIFRurgXd
# wAXXyXYcSmkOTbPqrjSwsbs8CuSwGqebbRSDKfjRzDqQ9D1AZ/JHHaaUkBbAYBsV
# MrvypDSrP/1o37mt4Zky28BnEp5ztEGp0HJ44X4rFVWWz+BfeuZWcVUcGKW2YFHo
# bNwGmJ/OanLvlnmtpZIRLF9ZkbzCHHomi+RId4g3fc3FsGxKqEW9Vj8PCumwKc6L
# UwZU4wIDAQABo4IBiTCCAYUwHwYDVR0jBBgwFoAUDyrLIIcouOxvSK4rVKYpqhek
# zQwwHQYDVR0OBBYEFCiCHmEfvPkU1uIc2sPugFDBq88SMA4GA1UdDwEB/wQEAwIH
# gDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMEoGA1UdIARDMEEw
# NQYMKwYBBAGyMQECAQMCMCUwIwYIKwYBBQUHAgEWF2h0dHBzOi8vc2VjdGlnby5j
# b20vQ1BTMAgGBmeBDAEEATBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLnNl
# Y3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ0NBUjM2LmNybDB5Bggr
# BgEFBQcBAQRtMGswRAYIKwYBBQUHMAKGOGh0dHA6Ly9jcnQuc2VjdGlnby5jb20v
# U2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nQ0FSMzYuY3J0MCMGCCsGAQUFBzABhhdo
# dHRwOi8vb2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAYEAmLUUP/C5
# nHN/qX27dIrfNezHdUul/uhOA5CwNkD7P4pvLJButR/S1OmvozuzJJTce6824Iyl
# nXkRwUFj04XLbodkBL7+YwQ5ml7CjdDSVo+sI/38jcEQ6FgosV/TTJSiFAgqMNwk
# x/kSzvQ1/Ufp5YVKggCXGJ4VitIzl5nMbzzu35G/uy4vmCQfh0KPYUTJYiRsF6Z3
# XJiIVtYrEwN/ikif/WFGrzsFj1OOWHNn5qDOP80xExmRS09z/wdZE9RdjPv5fYLn
# KWy1+GQ/w1vzg/l2vUXIgBV0MxalUfTP4V9Spsodrb+noPXiCy5n+6hy9yCf3EQb
# 3G1n8rT/a454fLSijMm6bhrgBRqhPUUtn6ZIBdEJzJUI6ftuXrQnB/U7zf32xcTT
# AW7WPem7DFK/4JrSaxiXcSkxQ4kXJDVoDPUJdpb0c5XdWVJO0DCkB35ONEIoqT6V
# jEIjLPSw9UXE420r1OIpV8FRJqrW4Fr5RUveEUlyF+FyygVOYZECNsjRMIIGYjCC
# BMqgAwIBAgIRAKQpO24e3denNAiHrXpOtyQwDQYJKoZIhvcNAQEMBQAwVTELMAkG
# A1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEsMCoGA1UEAxMjU2Vj
# dGlnbyBQdWJsaWMgVGltZSBTdGFtcGluZyBDQSBSMzYwHhcNMjUwMzI3MDAwMDAw
# WhcNMzYwMzIxMjM1OTU5WjByMQswCQYDVQQGEwJHQjEXMBUGA1UECBMOV2VzdCBZ
# b3Jrc2hpcmUxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEwMC4GA1UEAxMnU2Vj
# dGlnbyBQdWJsaWMgVGltZSBTdGFtcGluZyBTaWduZXIgUjM2MIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEA04SV9G6kU3jyPRBLeBIHPNyUgVNnYayfsGOy
# YEXrn3+SkDYTLs1crcw/ol2swE1TzB2aR/5JIjKNf75QBha2Ddj+4NEPKDxHEd4d
# En7RTWMcTIfm492TW22I8LfH+A7Ehz0/safc6BbsNBzjHTt7FngNfhfJoYOrkugS
# aT8F0IzUh6VUwoHdYDpiln9dh0n0m545d5A5tJD92iFAIbKHQWGbCQNYplqpAFas
# HBn77OqW37P9BhOASdmjp3IijYiFdcA0WQIe60vzvrk0HG+iVcwVZjz+t5OcXGTc
# xqOAzk1frDNZ1aw8nFhGEvG0ktJQknnJZE3D40GofV7O8WzgaAnZmoUn4PCpvH36
# vD4XaAF2CjiPsJWiY/j2xLsJuqx3JtuI4akH0MmGzlBUylhXvdNVXcjAuIEcEQKt
# OBR9lU4wXQpISrbOT8ux+96GzBq8TdbhoFcmYaOBZKlwPP7pOp5Mzx/UMhyBA93P
# QhiCdPfIVOCINsUY4U23p4KJ3F1HqP3H6Slw3lHACnLilGETXRg5X/Fp8G8qlG5Y
# +M49ZEGUp2bneRLZoyHTyynHvFISpefhBCV0KdRZHPcuSL5OAGWnBjAlRtHvsMBr
# I3AAA0Tu1oGvPa/4yeeiAyu+9y3SLC98gDVbySnXnkujjhIh+oaatsk/oyf5R2vc
# xHahajMCAwEAAaOCAY4wggGKMB8GA1UdIwQYMBaAFF9Y7UwxeqJhQo1SgLqzYZcZ
# ojKbMB0GA1UdDgQWBBSIYYyhKjdkgShgoZsx0Iz9LALOTzAOBgNVHQ8BAf8EBAMC
# BsAwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDBKBgNVHSAE
# QzBBMDUGDCsGAQQBsjEBAgEDCDAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3Rp
# Z28uY29tL0NQUzAIBgZngQwBBAIwSgYDVR0fBEMwQTA/oD2gO4Y5aHR0cDovL2Ny
# bC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGljVGltZVN0YW1waW5nQ0FSMzYuY3Js
# MHoGCCsGAQUFBwEBBG4wbDBFBggrBgEFBQcwAoY5aHR0cDovL2NydC5zZWN0aWdv
# LmNvbS9TZWN0aWdvUHVibGljVGltZVN0YW1waW5nQ0FSMzYuY3J0MCMGCCsGAQUF
# BzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAYEA
# AoE+pIZyUSH5ZakuPVKK4eWbzEsTRJOEjbIu6r7vmzXXLpJx4FyGmcqnFZoa1dzx
# 3JrUCrdG5b//LfAxOGy9Ph9JtrYChJaVHrusDh9NgYwiGDOhyyJ2zRy3+kdqhwtU
# lLCdNjFjakTSE+hkC9F5ty1uxOoQ2ZkfI5WM4WXA3ZHcNHB4V42zi7Jk3ktEnkSd
# ViVxM6rduXW0jmmiu71ZpBFZDh7Kdens+PQXPgMqvzodgQJEkxaION5XRCoBxAwW
# wiMm2thPDuZTzWp/gUFzi7izCmEt4pE3Kf0MOt3ccgwn4Kl2FIcQaV55nkjv1gOD
# cHcD9+ZVjYZoyKTVWb4VqMQy/j8Q3aaYd/jOQ66Fhk3NWbg2tYl5jhQCuIsE55Vg
# 4N0DUbEWvXJxtxQQaVR5xzhEI+BjJKzh3TQ026JxHhr2fuJ0mV68AluFr9qshgwS
# 5SpN5FFtaSEnAwqZv3IS+mlG50rK7W3qXbWwi4hmpylUfygtYLEdLQukNEX1jiOK
# MIIGgjCCBGqgAwIBAgIQNsKwvXwbOuejs902y8l1aDANBgkqhkiG9w0BAQwFADCB
# iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
# cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
# BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMjEw
# MzIyMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjBXMQswCQYDVQQGEwJHQjEYMBYGA1UE
# ChMPU2VjdGlnbyBMaW1pdGVkMS4wLAYDVQQDEyVTZWN0aWdvIFB1YmxpYyBUaW1l
# IFN0YW1waW5nIFJvb3QgUjQ2MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
# AgEAiJ3YuUVnnR3d6LkmgZpUVMB8SQWbzFoVD9mUEES0QUCBdxSZqdTkdizICFNe
# INCSJS+lV1ipnW5ihkQyC0cRLWXUJzodqpnMRs46npiJPHrfLBOifjfhpdXJ2aHH
# sPHggGsCi7uE0awqKggE/LkYw3sqaBia67h/3awoqNvGqiFRJ+OTWYmUCO2GAXse
# PHi+/JUNAax3kpqstbl3vcTdOGhtKShvZIvjwulRH87rbukNyHGWX5tNK/WABKf+
# Gnoi4cmisS7oSimgHUI0Wn/4elNd40BFdSZ1EwpuddZ+Wr7+Dfo0lcHflm/FDDrO
# J3rWqauUP8hsokDoI7D/yUVI9DAE/WK3Jl3C4LKwIpn1mNzMyptRwsXKrop06m7N
# UNHdlTDEMovXAIDGAvYynPt5lutv8lZeI5w3MOlCybAZDpK3Dy1MKo+6aEtE9vti
# TMzz/o2dYfdP0KWZwZIXbYsTIlg1YIetCpi5s14qiXOpRsKqFKqav9R1R5vj3Nge
# vsAsvxsAnI8Oa5s2oy25qhsoBIGo/zi6GpxFj+mOdh35Xn91y72J4RGOJEoqzEIb
# W3q0b2iPuWLA911cRxgY5SJYubvjay3nSMbBPPFsyl6mY4/WYucmyS9lo3l7jk27
# MAe145GWxK4O3m3gEFEIkv7kRmefDR7Oe2T1HxAnICQvr9sCAwEAAaOCARYwggES
# MB8GA1UdIwQYMBaAFFN5v1qqK0rPVIDh2JvAnfKyA2bLMB0GA1UdDgQWBBT2d2rd
# P/0BE/8WoWyCAi/QCj0UJTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
# /zATBgNVHSUEDDAKBggrBgEFBQcDCDARBgNVHSAECjAIMAYGBFUdIAAwUAYDVR0f
# BEkwRzBFoEOgQYY/aHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdFJT
# QUNlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMDUGCCsGAQUFBwEBBCkwJzAlBggr
# BgEFBQcwAYYZaHR0cDovL29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwF
# AAOCAgEADr5lQe1oRLjlocXUEYfktzsljOt+2sgXke3Y8UPEooU5y39rAARaAdAx
# UeiX1ktLJ3+lgxtoLQhn5cFb3GF2SSZRX8ptQ6IvuD3wz/LNHKpQ5nX8hjsDLRhs
# yeIiJsms9yAWnvdYOdEMq1W61KE9JlBkB20XBee6JaXx4UBErc+YuoSb1SxVf7nk
# NtUjPfcxuFtrQdRMRi/fInV/AobE8Gw/8yBMQKKaHt5eia8ybT8Y/Ffa6HAJyz9g
# vEOcF1VWXG8OMeM7Vy7Bs6mSIkYeYtddU1ux1dQLbEGur18ut97wgGwDiGinCwKP
# yFO7ApcmVJOtlw9FVJxw/mL1TbyBns4zOgkaXFnnfzg4qbSvnrwyj1NiurMp4pmA
# WjR+Pb/SIduPnmFzbSN/G8reZCL4fvGlvPFk4Uab/JVCSmj59+/mB2Gn6G/UYOy8
# k60mKcmaAZsEVkhOFuoj4we8CYyaR9vd9PGZKSinaZIkvVjbH/3nlLb0a7SBIkiR
# zfPfS9T+JesylbHa1LtRV9U/7m0q7Ma2CQ/t392ioOssXW7oKLdOmMBl14suVFBm
# bzrt5V5cQPnwtd3UOTpS9oCG+ZZheiIvPgkDmA8FzPsnfXW5qHELB43ET7HHFHeR
# PRYrMBKjkb8/IN7Po0d0hQoF4TeMM+zYAJzoKQnVKOLg8pZVPT8xggYwMIIGLAIB
# ATBoMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzAp
# BgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYCEGnLOUg5
# Lzbon9GhJmZq9N4wCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKEC
# gAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwG
# CisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFAKoak1iQcwSDWSkPoebf0GjYSGS
# MA0GCSqGSIb3DQEBAQUABIICAAAgpTIo5WkhfdkvedVydI5vxaNWXNGltd+6WXCD
# kdRI0cnPMIGahAi6CaL5tGY0DzVHFu3odt0y5Hul4WRHqcroNT8/Zw1F3ZfroqcB
# sr+cP+5lytlYzvbFE44DOBtUC/lut4kYQlJXOca/gWHRZFyiOtih5BwkiPh4jQMK
# n9/bZ2kX8eUuaBvkw5gHP2P/lsZ2G+YOgBK2KNgXfilCJm2xIWm8XOgUzpPig7Q8
# /YOVh5uJ5RIvHHsBe0w+AdbBD5iemgi0hOt7JYCZprwD30dfQGqVexIg2x+W/fUg
# hp9GnBgGvSHA3I+LyC3X2Hq/1KTSMqbTRYiEAPEukrSLKMYoRGwES7FE/tDuFl2C
# ZIdl04z9EIkg3AvHvAEOYpd4W17Ko2P6LMtHFOuiOGWd/EqSV1pxS+gbSQJEUg9u
# 74ltSjiiPjbD309dzNeuP8Q72bkAesrYSBlQUEBaSG03hanu6n4ZOiD9Hhr+b6Mi
# 0aMiEKLuQ1bT0e959kImS/U+IojW8ZRpWis5mCzQktMVAyBs0141ah6p9tuWBlKO
# j2Vnn22409lAdUVad4jKrIQ8cRKtf7Y7e/oYA+Zsiadvvm00MTXObTSHl2jY/wQI
# bDPinllRkmSRu7Rq+8Xx2HfLPzv7aVWZiCHschRxYqEyF48EptakkbaW05bQkoI1
# VqUaoYIDIzCCAx8GCSqGSIb3DQEJBjGCAxAwggMMAgEBMGowVTELMAkGA1UEBhMC
# R0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEsMCoGA1UEAxMjU2VjdGlnbyBQ
# dWJsaWMgVGltZSBTdGFtcGluZyBDQSBSMzYCEQCkKTtuHt3XpzQIh616TrckMA0G
# CWCGSAFlAwQCAgUAoHkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG
# 9w0BCQUxDxcNMjUwNzE3MDQ1ODQwWjA/BgkqhkiG9w0BCQQxMgQwmtSqrZ0U7Z6C
# xJpLZv+psX/LeaVK5AoqNUnMTVoSYqXbDLtX32/RfVShYkKIRVVQMA0GCSqGSIb3
# DQEBAQUABIICAJITZE0TyShPYjLRjkc7QbmHoyasUNvfD8tkZDD4X2WGzb+srSRr
# YOgJ3qBYR4/ADzdUBfTdWqFVdVV7l+qy3Pn9H5R5f4ttzDQnRNXJn4uVZ31z/dHF
# wUAKr1ZbikOBv1xoF7m0SShXKdohP5dn0hOscFTvf87s52Yh9McNEqe7MFx4vCqk
# PUyqmi1wh2SOaXnS3GylP9AzpaePXxlp60ZAWZ3sda0TXk6VsmVq1IM5ftdnGHSQ
# dOMoC70sMP7HVvviUzMs+SMhjQuuNmst0mJq/R6skDWTwdAOT24rJpInL84SlbT5
# 6TpF7JHLnwOlKnDYTRdfjRQ4FJTlWh7KHKj0Ofj11fZOicvFNzjhA5GFxHMF/SpL
# ITG+p6c4E/sQqapjS3e1RLLBOhs+uXITSoA5ck/e0J4JU9V9YC76ZtV3a/an4hSQ
# 9/tUU5xoe69VPzpmCsRFjlP2HhZ5oaFASGqV+yRcDPJuhehy9wg+G5QqKhajzsE/
# 6YHBWi9ehf6OMB3z7eCE6xcC6wo9jwcYeCHyuPxDUjzSBKayzBlFerlmYc2NqTbl
# jTZ7JfoUbhwlKeGh2zRE7h4bOnOWR1A7FdnXrJxEpoDxDcqN5oKIWC86WkzbkUj1
# skQvXTI3PNNtF/FF39JsccMyZstRoZWlDQo5QxbjsSEad+jzs0TPMVOx
# SIG # End signature block