public/Invoke-VPASMetricsPlatforms.ps1
<#
.Synopsis RUN VARIOUS PLATFORM METRICS FROM CYBERARK CREATED BY: Vadim Melamed, EMAIL: vmelamed5@gmail.com .DESCRIPTION USE THIS FUNCTION TO GENERATE VARIOUS PLATFORM 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: 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 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-VPASMetricsPlatforms -TargetMetric AccountsAssignedToPlatforms -OutputDirectory "C:\temp\VPASMetrics" -MetricFormat ALL -HTMLChart ALL .OUTPUTS HashTable object if successful $false if failed #> function Invoke-VPASMetricsPlatforms{ [OutputType([bool])] [CmdletBinding()] Param( [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,HelpMessage="Enter TargetMetric to be generated (AccountsAssignedToPlatforms, AutomaticVsManualRotation, AutomaticVsManualVerification)",Position=0)] [ValidateSet('AccountsAssignedToPlatforms','AutomaticVsManualRotation','AutomaticVsManualVerification')] [String]$TargetMetric, [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,HelpMessage="Enter ReportOutput type (JSON, HTML, ALL, NONE)",Position=1)] [ValidateSet('JSON','HTML','ALL','NONE')] [String]$MetricFormat, [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true,Position=2)] [String]$OutputDirectory, [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true,Position=3)] [ValidateSet('BarGraph','LineGraph','PieChart','ALL')] [String]$HTMLChart, [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true,Position=4)] [switch]$HideRawData, [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true,Position=5)] [hashtable]$token ) Begin{ $tokenval,$sessionval,$PVWA,$Header,$ISPSS,$IdentityURL,$EnableTextRecorder,$AuditTimeStamp,$NoSSL,$VaultVersion,$HideWarnings,$AuthenticatedAs,$SubDomain = Get-VPASSession -token $token $CommandName = $MyInvocation.MyCommand.Name $log = Write-VPASTextRecorder -inputval $CommandName -token $token -LogType COMMAND } Process{ Write-Verbose "SUCCESSFULLY PARSED PVWA VALUE" Write-Verbose "SUCCESSFULLY PARSED TOKEN VALUE" Write-Verbose "SUCCESSFULLY PARSED TARGET METRIC VALUE: $TargetMetric" Write-Verbose "SUCCESSFULLY PARSED METRIC OUTPUT VALUE: $MetricFormat" try{ if($MetricFormat -ne "NONE"){ if([String]::IsNullOrEmpty($OutputDirectory)){ $curUser = $env:UserName $OutputDirectory = "C:\Users\$curUser\AppData\Local\VPASModuleOutputs\Metrics" Write-Verbose "NO OUTPUT DIRECTORY SUPPLIED, USING DEFAULT LOCATION: $OutputDirectory" if(Test-Path -Path $OutputDirectory){ #DO NOTHING } else{ write-verbose "$OutputDirectory DOES NOT EXIST, CREATING DIRECTORY" $MakeDirectory = New-Item -Path $OutputDirectory -Type Directory } } else{ if(Test-Path -Path $OutputDirectory){ #DO NOTHING } else{ $curUser = $env:UserName $OutputDirectory = "C:\Users\$curUser\AppData\Local\VPASModuleOutputs\Metrics" write-verbose "$OutputDirectory DOES NOT EXIST, USING DEFAULT LOCATION: $OutputDirectory" if(Test-Path -Path $OutputDirectory){ #DO NOTHING } else{ $MakeDirectory = New-Item -Path $OutputDirectory -Type Directory } } } } if([String]::IsNullOrEmpty($HTMLChart)){ $HTMLChart = "BarGraph" } 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){ $targetPlatformID = $acct.platformId 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) } } } } $AllPlatforms = Get-VPASAllPlatforms foreach($plat in $AllPlatforms.Platforms){ $targetplatID = $plat.general.id 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){ $targetPlatformID = $acct.platformId 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){ $targetPlatformID = $acct.platformId 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 } } } $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> 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> $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> 1)</small></span> <small>$recommendation1</small> </div> <div class=`"metric`"> <span class=`"metric-label`"><small> 2)</small></span> <small>$recommendation2</small> </div> <div class=`"metric`"> <span class=`"metric-label`"><small> 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 } } |