public/Invoke-VPASHealthCheck.ps1

<#
.Synopsis
   RUN A SYSTEM HEALTHCHECK
   CREATED BY: Vadim Melamed, EMAIL: vpasmodule@gmail.com
.DESCRIPTION
   USE THIS FUNCTION TO RUN A HEALTHCHECK THAT RUNS VARIOUS CHECKS IN A CYBERARK ENVIRONMENT
.LINK
   https://vpasmodule.com/commands/Invoke-VPASHealthCheck
.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 ExportToCSV
   Output results to a CSV file found in AppData\Local\VPASModuleOutputs directory
.PARAMETER CSVDirectory
   Output directory where the exported CSV file will be saved to
   Default value: C:\Users\{CurrentUser}\AppData\Local\VPASModuleOutputs\ExportedCSVs
.PARAMETER InputParameters
   HashTable of values containing the parameters required to make the API call
.EXAMPLE
   $HealthCheck = Invoke-VPASHealthCheck
.EXAMPLE
   $InputParameters = @{
        ExportToCSV = $true|$false
        CSVDirectory = "C:\Temp"
   }
   $HealthCheck = Invoke-VPASHealthCheck -InputParameters $InputParameters
.OUTPUTS
   If successful:
   {
        ...
        "UnusedAssetsCheck": [
                              ...
                              {
                                  "TargetName": "UnusedAppID",
                                  "Category": "ApplicationID",
                                  "Recommendation": "Recommendation: delete unused application IDs (UnusedAppID) to help reduce clutter",
                                  "Check": "UnusedAssetsCheck"
                              },
                              {
                                  "TargetName": "UnusedSafe1",
                                  "Category": "Safe",
                                  "Recommendation": "Recommendation: delete unused Safes (UnusedSafe1) to help reduce clutter",
                                  "Check": "UnusedAssetsCheck"
                              },
                              ...
   }
   ---
   $false if failed
#>

function Invoke-VPASHealthCheck{
    [OutputType('System.Object',[bool])]
    [CmdletBinding(DefaultParameterSetName='Set1')]
    Param(
        [Parameter(Mandatory=$false,ParameterSetName='Set1',ValueFromPipelineByPropertyName=$true)]
        [Switch]$ExportToCSV,

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

        [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,Position=0)]
        [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 = @("ExportToCSV","CSVDirectory")
                        MandatoryKeys = @()
                    }
                }
                $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 GENERATE HEALTHCHECK REPORT"
            Write-VPASOutput -str $_ -type E
            return $false
        }

        try{
            Write-VPASOutput -str "***INITIALIZING HEALTHCHECK VARIABLES***" -type G
            $outputmatrix = @{
                VersionCheck = @()
                ComponentStatusCheck = @()
                EmptySafeCheck = @()
                ApplicationAuthMethodCheck = @()
                AccountComplianceCheck = @()
                InactivePlatformCheck = @()
                UnusedAssetsCheck = @()
                InactiveUserCheck = @()
                UnknownAssetCheck = @()
            }
            $onpremflag = $false
            if(!$ISPSS -and ($PVWA -notmatch ".privilegecloud.cyberark.")){
                $onpremflag = $true
            }

            $SystemComponents = @{
                CPM = @{
                    UniqueNames = @()
                }
                PVWA = @{
                    UniqueNames = @()
                }
                AIMPROV = @{
                    UniqueNames = @()
                }
                PSM = @{
                    UniqueNames = @()
                }
                VAULT = @{
                    Version = 0
                }
                APPID = @{
                    Uniquenames = @()
                }
                SAFE = @{
                    Uniquenames = @()
                }
                PLATFORM = @{
                    Uniquenames = @()
                }
                PSMSERVERID = @{
                    Uniquenames = @()
                }
                CONNECTIONCOMPONENT = @{
                    Uniquenames = @()
                }
                ACCOUNT = @{

                }
            }
            Write-VPASOutput -str "*Note, some of the checks below may take some time depending on the environment size" -type C
            #region VersionCheck
            Write-VPASOutput -str "***RUNNING COMPONENT VERSION + HEALTH CHECKS***" -type G
            $AllCPMs = Get-VPASSystemHealth -Component CPM
            $AllPSMs = Get-VPASSystemHealth -Component PSM
            $AllPVWAs = Get-VPASSystemHealth -Component PVWA
            $AllAIMs = Get-VPASSystemHealth -Component AIM
            $VaultVersion = (Get-VPASVaultVersion).ExternalVersion

            if(!$VaultVersion){
                $SystemComponents.Vault = @{
                    Version = "UNCLEAR"
                }
            }
            else{
                $tempversionarr = $VaultVersion -split "\."
                $VaultVersion = [Double]($tempversionarr[0] + "." + $tempversionarr[1])
                $SystemComponents.Vault = @{
                    Version = $VaultVersion
                }
            }

            foreach($rec in $AllCPMs.ComponentsDetails){
                $ComponentName = $rec.ComponentUserName
                $ComponentVersion = $rec.ComponentVersion
                $ComponentStatus = $rec.IsLoggedOn
                $ComponentIP = $rec.ComponentIP
                $tempversionarr = $ComponentVersion -split "\."
                $ComponentVersion = [Double]($tempversionarr[0] + "." + $tempversionarr[1])

                $SystemComponents.CPM += @{
                    $ComponentName = @{
                        ComponentUsername = $ComponentName
                        ComponentVersion = $ComponentVersion
                        ComponentStatus = $ComponentStatus
                        ComponentIP = $ComponentIP
                        SafeAccess = $false
                    }
                }
                $SystemComponents.CPM.UniqueNames += $ComponentName

                if($ComponentVersion -lt $VaultVersion){
                    $outputmatrix.VersionCheck += @{
                        Check = "VersionCheck"
                        Category = "CPM"
                        TargetName = $ComponentName
                        Recommendation = "Recommendation: update CPM $ComponentName@$ComponentIP from $ComponentVersion to match vault version $VaultVersion"
                    }
                }
                if(!$ComponentStatus){
                    $outputmatrix.ComponentStatusCheck += @{
                        Check = "ComponentStatusCheck"
                        Category = "CPM"
                        TargetName = $ComponentName
                        Recommendation = "Recommendation: reconnect CPM $ComponentName@$ComponentIP to restore functionality"
                    }
                }
            }

            foreach($rec in $AllPSMs.ComponentsDetails){
                $ComponentName = $rec.ComponentUserName
                $ComponentVersion = $rec.ComponentVersion
                $ComponentStatus = $rec.IsLoggedOn
                $ComponentIP = $rec.ComponentIP
                $tempversionarr = $ComponentVersion -split "\."
                $ComponentVersion = [Double]($tempversionarr[0] + "." + $tempversionarr[1])

                $SystemComponents.PSM += @{
                    $ComponentName = @{
                        ComponentUsername = $ComponentName
                        ComponentVersion = $ComponentVersion
                        ComponentStatus = $ComponentStatus
                        ComponentIP = $ComponentIP
                    }
                }
                $SystemComponents.PSM.UniqueNames += $ComponentName

                if($ComponentVersion -lt $VaultVersion){
                    $outputmatrix.VersionCheck += @{
                        Check = "VersionCheck"
                        Category = "PSM"
                        TargetName = $ComponentName
                        Recommendation = "Recommendation: update PSM $ComponentName@$ComponentIP from $ComponentVersion to match vault version $VaultVersion"
                    }
                }
                if(!$ComponentStatus){
                    $outputmatrix.ComponentStatusCheck += @{
                        Check = "ComponentStatusCheck"
                        Category = "PSM"
                        TargetName = $ComponentName
                        Recommendation = "Recommendation: reconnect PSM $ComponentName@$ComponentIP to restore functionality"
                    }
                }
            }

            if($onpremflag){
                foreach($rec in $AllPVWAs.ComponentsDetails){
                    $ComponentName = $rec.ComponentUserName
                    $ComponentVersion = $rec.ComponentVersion
                    $ComponentStatus = $rec.IsLoggedOn
                    $ComponentIP = $rec.ComponentIP
                    $tempversionarr = $ComponentVersion -split "\."
                    $ComponentVersion = [Double]($tempversionarr[0] + "." + $tempversionarr[1])

                    $SystemComponents.PVWA += @{
                        $ComponentName = @{
                            ComponentUsername = $ComponentName
                            ComponentVersion = $ComponentVersion
                            ComponentStatus = $ComponentStatus
                            ComponentIP = $ComponentIP
                        }
                    }
                    $SystemComponents.PVWA.UniqueNames += $ComponentName

                    if($ComponentVersion -lt $VaultVersion){
                        $outputmatrix.VersionCheck += @{
                            Check = "VersionCheck"
                            Category = "PVWA"
                            TargetName = $ComponentName
                            Recommendation = "Recommendation: update PVWA $ComponentName@$ComponentIP from $ComponentVersion to match vault version $VaultVersion"
                        }
                    }
                    if(!$ComponentStatus){
                        $outputmatrix.ComponentStatusCheck += @{
                            Check = "ComponentStatusCheck"
                            Category = "PVWA"
                            TargetName = $ComponentName
                            Recommendation = "Recommendation: reconnect PVWA $ComponentName@$ComponentIP to restore functionality"
                        }
                    }
                }
            }

            foreach($rec in $AllAIMs.ComponentsDetails){
                $ComponentName = $rec.ComponentUserName
                $ComponentVersion = $rec.ComponentVersion
                $ComponentStatus = $rec.IsLoggedOn
                $ComponentIP = $rec.ComponentIP
                $tempversionarr = $ComponentVersion -split "\."
                $ComponentVersion = [Double]($tempversionarr[0] + "." + $tempversionarr[1])

                $SystemComponents.AIMPROV += @{
                    $ComponentName = @{
                        ComponentUsername = $ComponentName
                        ComponentVersion = $ComponentVersion
                        ComponentStatus = $ComponentStatus
                        ComponentIP = $ComponentIP
                        SafeAccess = $false
                        IsGroup = $false
                    }
                }
                $SystemComponents.AIMPROV.UniqueNames += $ComponentName

                $epvgroups = (Get-VPASEPVUserDetails -EPVUsername $ComponentName).groupsMembership
                foreach($grouprec in $epvgroups){
                    $group = $grouprec.groupName
                    if($SystemComponents.AIMPROV.$group){
                        $SystemComponents.AIMPROV.$group.Providers += $ComponentName
                    }
                    else{
                        $SystemComponents.AIMPROV += @{
                            $group = @{
                                IsGroup = $true
                                Providers = @($ComponentName)
                                SafeAccess = $false
                            }
                        }
                        $SystemComponents.AIMPROV.Uniquenames += $group
                    }
                }

                if($ComponentVersion -lt $VaultVersion){
                    $outputmatrix.VersionCheck += @{
                        Check = "VersionCheck"
                        Category = "AIM"
                        TargetName = $ComponentName
                        Recommendation = "Recommendation: update AIM Provider $ComponentName@$ComponentIP from $ComponentVersion to match vault version $VaultVersion"
                    }
                }
                if(!$ComponentStatus){
                    $outputmatrix.ComponentStatusCheck += @{
                        Check = "ComponentStatusCheck"
                        Category = "AIM"
                        TargetName = $ComponentName
                        Recommendation = "Recommendation: reconnect AIM Provider $ComponentName@$ComponentIP to restore functionality"
                    }
                }
            }
            #endregion VersionCheck
            #region ApplicationCheck
            Write-VPASOutput -str "***RUNNING APPLICATION HEALTH CHECKS***" -type G
            if($SystemComponents.AIMPROV.UniqueNames.Count -gt 0){
                $AllApplications = Get-VPASAllApplications
                foreach($app in $AllApplications){
                    $appname = $app.AppID

                    if($appname -eq "AIMWebService"){
                        #DO NOTHING
                    }
                    else{
                        $SystemComponents.APPID += @{
                            $appname = @{
                                AppID = $appname
                                SafeAccess = $false
                                IsGroup = $false
                            }
                        }
                        $SystemComponents.APPID.Uniquenames += $appname

                        $epvgroups = (Get-VPASEPVUserDetails -EPVUsername $appname).groupsMembership
                        foreach($grouprec in $epvgroups){
                            $group = $grouprec.groupName
                            if($SystemComponents.APPID.$group){
                                $SystemComponents.APPID.$group.AppIDs += $appname
                            }
                            else{
                                $SystemComponents.APPID += @{
                                    $group = @{
                                        IsGroup = $true
                                        AppIDs = @($appname)
                                        SafeAccess = $false
                                    }
                                }
                                $SystemComponents.APPID.Uniquenames += $group
                            }
                        }

                        $ApplicationAuthmethods = Get-VPASApplicationAuthentications -AppID $appname
                        if($ApplicationAuthmethods.authentication.Count -eq 0){
                            $outputmatrix.ApplicationAuthMethodCheck += @{
                                Check = "ApplicationAuthMethodCheck"
                                Category = "Application"
                                TargetName = $appname
                                Recommendation = "Recommendation: Using an application with no authentication method configured is not recommended in any scenario. Add an authentication method to ApplicationID: $appname"
                            }
                        }
                    }
                }
            }
            #endregion ApplicationCheck
            #region SafeCheck
            Write-VPASOutput -str "***RUNNING SAFE + SAFE MEMBER HEALTH CHECKS***" -type G
            $SkipSafes = @(
                "VaultInternal",
                "Notification Engine",
                "SharedAuth_Internal",
                "PVWAReports",
                "PVWATicketingSystem",
                "PVWAPublicData",
                "TelemetryConfig",
                "PasswordManager_Pending",
                "AccountsFeedADAccounts",
                "AccountsFeedDiscoveryLogs",
                "PSM",
                "PSMUniversalConnectors",
                "PSMRecordings",
                "PVWAConfig",
                "PVWAUserPrefs",
                "PVWAPrivateUserPrefs",
                "PVWATaskDefinitions",
                "PSMSessions",
                "PSMLiveSessions",
                "PSMUnmanagedSessionAccounts",
                "PSMNotifications",
                "PSMPConf",
                "PSMPLiveSessions",
                "PSMPADBUserProfile",
                "PSMPADBridgeCustom",
                "PSMPADBridgeConf",
                "AppProviderCacheSafe"
            )
            foreach($val in $SystemComponents.CPM.UniqueNames){
                $SkipSafes += $val
                $SkipSafes += $val + "_Accounts"
            }

            $AllSafes = Get-VPASAllSafes -IncludeAccounts
            foreach($safe in $AllSafes.value){
                $safename = $safe.safeName
                if(!$SkipSafes.Contains($safename)){
                    $SystemComponents.SAFE.Uniquenames += $safename
                    $SystemComponents.SAFE += @{
                        $safename = @{
                            Accounts = @{}
                        }
                    }
                    #EMPTY SAFE CHECK
                    if($safe.accounts.count -eq 0){
                        $outputmatrix.EmptySafeCheck += @{
                            Check = "EmptySafeCheck"
                            Category = "Safe"
                            TargetName = $safename
                            Recommendation = "Recommendation: delete unused or empty safes ($safename) to help reduce clutter (assuming safe permissions allow viewing safe content)"
                        }
                    }
                    else{
                        foreach($acct in $safe.accounts.id){
                            $SystemComponents.SAFE.$safename.Accounts += @{
                                $acct = @{
                                    AcctID = $acct
                                }
                            }
                        }
                    }

                    #GET MANAGED CPM
                    $managedCPM = $safe.managingCPM
                    if(![String]::IsNullOrEmpty($managedCPM)){
                        $SystemComponents.CPM.$managedCPM.SafeAccess = $true
                    }

                    #CHECK SAFE MEMBERS
                    $AllSafeMembers = Get-VPASSafeMembers -safe $safename
                    foreach($mem in $AllSafeMembers.value.membername){
                        if($SystemComponents.APPID.Uniquenames.Contains($mem)){
                            $SystemComponents.APPID.$mem.SafeAccess = $true
                        }
                        if($SystemComponents.AIMPROV.UniqueNames.Contains($mem)){
                            $SystemComponents.AIMPROV.$mem.SafeAccess = $true
                        }
                    }
                }
            }
            #endregion SafeCheck
            #region PSMCheck
            Write-VPASOutput -str "***RUNNING PSM + CONNECTOR HEALTH CHECKS***" -type G

            $AllPSMServerIDs = Get-VPASAllPSMServers
            foreach($PSMServerID in $AllPSMServerIDs.PSMServers.ID){
                $SystemComponents.PSMSERVERID += @{
                    $PSMServerID = @{
                        PSMServerID = $PSMServerID
                        InUse = $false
                    }
                }
                $SystemComponents.PSMSERVERID.Uniquenames += $PSMServerID
            }

            $AllConnectors = Get-VPASAllConnectionComponents
            foreach($connector in $AllConnectors.PSMConnectors.ID){
                if($connector -match "sample"){
                    $SystemComponents.CONNECTIONCOMPONENT += @{
                        $connector = @{
                            ConnectorID = $connector
                            InUse = $true
                        }
                    }
                }
                else{
                    $SystemComponents.CONNECTIONCOMPONENT += @{
                        $connector = @{
                            ConnectorID = $connector
                            InUse = $false
                        }
                    }
                }
                $SystemComponents.CONNECTIONCOMPONENT.Uniquenames += $connector
            }

            #endregion PSMCheck
            #region AccountCheck
            Write-VPASOutput -str "***RUNNING ACCOUNT HEALTH CHECKS***" -type G
            $ignoreplatforms = @("EPMLCDKey")
            $AllPlatforms = Get-VPASAllTargetPlatforms
            foreach($plat in $AllPlatforms.Platforms){
                $platID = $plat.PlatformID
                $platStatus = $plat.Active
                $platPSM = $plat.PrivilegedSessionManagement.PSMServerId
                $SystemComponents.PLATFORM.Uniquenames += $platID
                $SystemComponents.PLATFORM += @{
                    $platID = @{
                        platformID = $platID
                        platformStatus = $platStatus
                        platformInUse = $false
                    }
                }

                if(![String]::IsNullOrEmpty($platID)){
                    if(![String]::IsNullOrEmpty($platPSM)){
                        if($SystemComponents.PSMSERVERID.$platPSM){
                            $SystemComponents.PSMSERVERID.$platPSM.InUse = $true
                        }
                        else{
                            $outputmatrix.UnknownAssetCheck += @{
                                Check = "UnknownAssetCheck"
                                Category = "PSM"
                                TargetName = "$platPSM"
                                Recommendation = "Recommendation: unknown PSMServerID assigned to platform $platID (PSMServerID: $platPSM)...assign an existing PSMServerID to restore functionality"
                            }
                        }
                    }

                    $platdetails = Get-VPASPSMSettingsByPlatformID -PlatformID $platID
                    foreach($con in $platdetails.PSMConnectors.PSMConnectorID){
                        if($SystemComponents.CONNECTIONCOMPONENT.$con){
                            $SystemComponents.CONNECTIONCOMPONENT.$con.InUse = $true
                        }
                        else{
                            $outputmatrix.UnknownAssetCheck += @{
                                Check = "UnknownAssetCheck"
                                Category = "PSMConnector"
                                TargetName = "$con"
                                Recommendation = "Recommendation: unknown PSMConnector assigned to platform $platID (PSMConnector: $con). Remove the connector from the platform to clear up clutter"
                            }
                        }
                    }
                }
            }

            foreach($safe in $SystemComponents.SAFE.Keys){
                #LOOP THROUGH EVERY SAFE
                if($safe -ne "Uniquenames"){
                    foreach($AcctID in $SystemComponents.SAFE.$safe.Accounts.Keys){
                        $AcctDetails = Get-VPASAccountDetailsExtended -AcctID $AcctID
                        $IsCompliant = $AcctDetails.Compliance.IsCompliant
                        $targetPlat = $AcctDetails.Platform.PlatformID
                        $platstatus = $AcctDetails.Platform.IsActive
                        $acctname = $AcctDetails.Details.Name

                        if([String]::IsNullOrEmpty($targetPlat)){
                            $outputmatrix.InactivePlatformCheck += @{
                                Check = "InactivePlatformCheck"
                                Category = "Account"
                                TargetName = $acctname
                                Recommendation = "Recommendation: accounts attached to an inactive or blank platform will remain non functional. Move $acctname from {BlankPlatform} to an active platfrom"
                            }
                        }
                        elseif($ignoreplatforms.Contains($targetPlat)){
                            #DO NOTHING
                        }
                        else{
                            if($SystemComponents.PLATFORM.$targetPlat){
                                $SystemComponents.PLATFORM.$targetPlat.platformInUse = $true
                            }
                            else{
                                $SystemComponents.PLATFORM += @{
                                    $targetPlat = @{
                                        platformID = $targetPlat
                                        platformStatus = $platStatus
                                        platformInUse = $true
                                    }
                                }
                            }

                            if(!$IsCompliant){
                                $outputmatrix.AccountComplianceCheck += @{
                                    Check = "AccountComplianceCheck"
                                    Category = "Account"
                                    TargetName = $acctname
                                    Recommendation = "Recommendation: resolve any errors associated with the account ($acctname) to restore compliance and enable rotation where applicable"
                                }
                            }
                            if(!$platStatus){
                                $outputmatrix.InactivePlatformCheck += @{
                                    Check = "InactivePlatformCheck"
                                    Category = "Account"
                                    TargetName = $acctname
                                    Recommendation = "Recommendation: accounts attached to an inactive or blank platform will remain non functional. Move $acctname from platform $targetPlat to an active platfrom"
                                }
                            }
                        }
                    }
                }
            }
            #endregion AccountCheck
            #region UnusedAssetCheck
            Write-VPASOutput -str "***RUNNING UNUSED ASSETS CHECKS***" -type G

            #APPIDS - Step1 Groups
            foreach($targetval in $SystemComponents.APPID.Keys){
                if($targetval -ne "Uniquenames"){
                    $inuse = $SystemComponents.APPID.$targetval.SafeAccess
                    $isgroup = $SystemComponents.APPID.$targetval.IsGroup

                    if($isgroup){
                        if($inuse){
                            foreach($appid in $SystemComponents.APPID.$targetval.AppIDs){
                                $SystemComponents.APPID.$appid.SafeAccess = $true
                            }
                        }
                    }
                }
            }
            #APPIDS - Step2 AppIDs
            foreach($targetval in $SystemComponents.APPID.Keys){
                if($targetval -ne "Uniquenames"){
                    $inuse = $SystemComponents.APPID.$targetval.SafeAccess
                    $isgroup = $SystemComponents.APPID.$targetval.IsGroup

                    if(!$isgroup){
                        if(!$inuse){
                            $outputmatrix.UnusedAssetsCheck += @{
                                Check = "UnusedAssetsCheck"
                                Category = "ApplicationID"
                                TargetName = $targetval
                                Recommendation = "Recommendation: delete unused application IDs ($targetval) to help reduce clutter"
                            }
                        }
                    }
                }
            }

            #PLATFORMS
            foreach($targetval in $SystemComponents.PLATFORM.Keys){
                if($targetval -ne "Uniquenames"){
                    if($ignoreplatforms.Contains($targetval)){
                        #DO NOTHING
                    }
                    else{
                        $inuse = $SystemComponents.PLATFORM.$targetval.platformInUse
                        $isactive = $SystemComponents.PLATFORM.$targetval.platformStatus

                        if($isactive){
                            if(!$inuse){
                                $outputmatrix.UnusedAssetsCheck += @{
                                    Check = "UnusedAssetsCheck"
                                    Category = "Platform"
                                    TargetName = $targetval
                                    Recommendation = "Recommendation: deactivate or delete unused platforms ($targetval) to help reduce clutter and CPM load"
                                }
                            }
                        }
                    }
                }
            }

            #AIMPROV - Step1 Groups
            foreach($targetval in $SystemComponents.AIMPROV.Keys){
                if($targetval -ne "Uniquenames"){
                    $inuse = $SystemComponents.AIMPROV.$targetval.SafeAccess
                    $isgroup = $SystemComponents.AIMPROV.$targetval.IsGroup

                    if($isgroup){
                        if($inuse){
                            foreach($appid in $SystemComponents.AIMPROV.$targetval.Providers){
                                $SystemComponents.AIMPROV.$appid.SafeAccess = $true
                            }
                        }
                    }
                }
            }
            #AIMPROV - Step2 Providers
            foreach($targetval in $SystemComponents.AIMPROV.Keys){
                if($targetval -ne "Uniquenames"){
                    $inuse = $SystemComponents.AIMPROV.$targetval.SafeAccess
                    $isgroup = $SystemComponents.AIMPROV.$targetval.IsGroup

                    if(!$isgroup){
                        if(!$inuse){
                            $outputmatrix.UnusedAssetsCheck += @{
                                Check = "UnusedAssetsCheck"
                                Category = "AIMProviders"
                                TargetName = $targetval
                                Recommendation = "Recommendation: delete/uninstall unused providers ($targetval) to help reduce clutter and to free up provider licenses"
                            }
                        }
                    }
                }
            }

            #CPMs
            foreach($targetval in $SystemComponents.CPM.Keys){
                if($targetval -ne "Uniquenames"){
                    $inuse = $SystemComponents.CPM.$targetval.SafeAccess
                    if(!$inuse){
                        $outputmatrix.UnusedAssetsCheck += @{
                            Check = "UnusedAssetsCheck"
                            Category = "CPM"
                            TargetName = $targetval
                            Recommendation = "Recommendation: delete/uninstall unused CPMs ($targetval) to help reduce clutter and to free up a CPM license"
                        }
                    }
                }
            }

            #PSMServerIDs
            foreach($targetval in $SystemComponents.PSMSERVERID.Keys){
                if($targetval -ne "Uniquenames"){
                    $inuse = $SystemComponents.PSMSERVERID.$targetval.InUse
                    if(!$inuse){
                        $outputmatrix.UnusedAssetsCheck += @{
                            Check = "UnusedAssetsCheck"
                            Category = "PSMServer"
                            TargetName = $targetval
                            Recommendation = "Recommendation: delete unused PSM configuration ($targetval) to help reduce clutter"
                        }
                    }
                }
            }

            #ConnectionComponents
            foreach($targetval in $SystemComponents.CONNECTIONCOMPONENT.Keys){
                if($targetval -ne "Uniquenames"){
                    $inuse = $SystemComponents.CONNECTIONCOMPONENT.$targetval.InUse
                    if(!$inuse){
                        $outputmatrix.UnusedAssetsCheck += @{
                            Check = "UnusedAssetsCheck"
                            Category = "ConnectionComponent"
                            TargetName = $targetval
                            Recommendation = "Recommendation: delete unused ConnectionComponent configurations ($targetval) to help reduce clutter"
                        }
                    }
                }
            }
            #endregion UnusedAssetCheck
            #region UserCheck
            Write-VPASOutput -str "***RUNNING USER HEALTH CHECKS***" -type G
            $curTime = ([int][double]::Parse((Get-Date (get-date (get-date).Date).ToLocalTime() -UFormat %s)))
            $cutTime = ([int][double]::Parse((Get-Date (get-date (get-date).Date.AddYears(-1)).ToLocalTime() -UFormat %s)))
            $ignoreusers = @()
            $AllUsers = Get-VPASAllEPVUsers
            foreach($user in $AllUsers.Users){
                $userusername = $user.username
                $userid = $user.id
                $usertype = $user.userType

                if($usertype -eq "EPVUser"){
                    $userdetails = Get-VPASEPVUserDetails -EPVUserID $userid
                    $lastlogin = $userdetails.lastSuccessfulLoginDate

                    if($lastlogin -le $cutTime){
                        $outputmatrix.InactivePlatformCheck += @{
                            Check = "InactiveUserCheck"
                            Category = "User"
                            TargetName = "$userusername"
                            Recommendation = "Recommendation: $userusername has not logged into CyberArk in the last year. Consider removing the user to free up a user license"
                        }
                    }
                }
            }
            #endregion UserCheck



            if($ExportToCSV){
                $runexport = Write-VPASExportCSV -Data $outputmatrix -CommandName $CommandName -CSVDirectory $CSVDirectory
            }
            $log = Write-VPASTextRecorder -inputval $outputmatrix -token $token -LogType RETURNARRAY
            Write-Verbose "PARSING DATA FROM CYBERARK"
            Write-Verbose "RETURNING JSON OBJECT"
            return $outputmatrix
        }catch{
            $log = Write-VPASTextRecorder -inputval $_ -token $token -LogType ERROR
            $log = Write-VPASTextRecorder -inputval "REST API COMMAND RETURNED: FALSE" -token $token -LogType MISC
            Write-Verbose "UNABLE TO GENERATE USER LICENSE REPORT"
            Write-VPASOutput -str $_ -type E
            return $false
        }
    }
    End{
        $log = Write-VPASTextRecorder -inputval $CommandName -token $token -LogType DIVIDER
    }
}

# SIG # Begin signature block
# MIIroAYJKoZIhvcNAQcCoIIrkTCCK40CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUpFQCTtMtQDRlY5z/6VLynnWB
# 8HeggiTbMIIFbzCCBFegAwIBAgIQSPyTtGBVlI02p8mKidaUFjANBgkqhkiG9w0B
# 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+FyygVOYZECNsjRMIIGXTCC
# BMWgAwIBAgIQOlJqLITOVeYdZfzMEtjpiTANBgkqhkiG9w0BAQwFADBVMQswCQYD
# VQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSwwKgYDVQQDEyNTZWN0
# aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5nIENBIFIzNjAeFw0yNDAxMTUwMDAwMDBa
# Fw0zNTA0MTQyMzU5NTlaMG4xCzAJBgNVBAYTAkdCMRMwEQYDVQQIEwpNYW5jaGVz
# dGVyMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxMDAuBgNVBAMTJ1NlY3RpZ28g
# UHVibGljIFRpbWUgU3RhbXBpbmcgU2lnbmVyIFIzNTCCAiIwDQYJKoZIhvcNAQEB
# BQADggIPADCCAgoCggIBAI3RZ/TBSJu9/ThJOk1hgZvD2NxFpWEENo0GnuOYloD1
# 1BlbmKCGtcY0xiMrsN7LlEgcyoshtP3P2J/vneZhuiMmspY7hk/Q3l0FPZPBllo9
# vwT6GpoNnxXLZz7HU2ITBsTNOs9fhbdAWr/Mm8MNtYov32osvjYYlDNfefnBajrQ
# qSV8Wf5ZvbaY5lZhKqQJUaXxpi4TXZKohLgxU7g9RrFd477j7jxilCU2ptz+d1OC
# zNFAsXgyPEM+NEMPUz2q+ktNlxMZXPF9WLIhOhE3E8/oNSJkNTqhcBGsbDI/1qCU
# 9fBhuSojZ0u5/1+IjMG6AINyI6XLxM8OAGQmaMB8gs2IZxUTOD7jTFR2HE1xoL7q
# vSO4+JHtvNceHu//dGeVm5Pdkay3Et+YTt9EwAXBsd0PPmC0cuqNJNcOI0XnwjE+
# 2+Zk8bauVz5ir7YHz7mlj5Bmf7W8SJ8jQwO2IDoHHFC46ePg+eoNors0QrC0PWnO
# gDeMkW6gmLBtq3CEOSDU8iNicwNsNb7ABz0W1E3qlSw7jTmNoGCKCgVkLD2FaMs2
# qAVVOjuUxvmtWMn1pIFVUvZ1yrPIVbYt1aTld2nrmh544Auh3tgggy/WluoLXlHt
# AJgvFwrVsKXj8ekFt0TmaPL0lHvQEe5jHbufhc05lvCtdwbfBl/2ARSTuy1s8CgF
# AgMBAAGjggGOMIIBijAfBgNVHSMEGDAWgBRfWO1MMXqiYUKNUoC6s2GXGaIymzAd
# BgNVHQ4EFgQUaO+kMklptlI4HepDOSz0FGqeDIUwDgYDVR0PAQH/BAQDAgbAMAwG
# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwSgYDVR0gBEMwQTA1
# BgwrBgEEAbIxAQIBAwgwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNv
# bS9DUFMwCAYGZ4EMAQQCMEoGA1UdHwRDMEEwP6A9oDuGOWh0dHA6Ly9jcmwuc2Vj
# dGlnby5jb20vU2VjdGlnb1B1YmxpY1RpbWVTdGFtcGluZ0NBUjM2LmNybDB6Bggr
# BgEFBQcBAQRuMGwwRQYIKwYBBQUHMAKGOWh0dHA6Ly9jcnQuc2VjdGlnby5jb20v
# U2VjdGlnb1B1YmxpY1RpbWVTdGFtcGluZ0NBUjM2LmNydDAjBggrBgEFBQcwAYYX
# aHR0cDovL29jc3Auc2VjdGlnby5jb20wDQYJKoZIhvcNAQEMBQADggGBALDcLsn6
# TzZMii/2yU/V7xhPH58Oxr/+EnrZjpIyvYTz2u/zbL+fzB7lbrPml8ERajOVbuda
# n6x08J1RMXD9hByq+yEfpv1G+z2pmnln5XucfA9MfzLMrCArNNMbUjVcRcsAr18e
# eZeloN5V4jwrovDeLOdZl0tB7fOX5F6N2rmXaNTuJR8yS2F+EWaL5VVg+RH8FelX
# tRvVDLJZ5uqSNIckdGa/eUFhtDKTTz9LtOUh46v2JD5Q3nt8mDhAjTKp2fo/KJ6F
# LWdKAvApGzjpPwDqFeJKf+kJdoBKd2zQuwzk5Wgph9uA46VYK8p/BTJJahKCuGdy
# KFIFfEfakC4NXa+vwY4IRp49lzQPLo7WticqMaaqb8hE2QmCFIyLOvWIg4837bd+
# 60FcCGbHwmL/g1ObIf0rRS9ceK4DY9rfBnHFH2v1d4hRVvZXyCVlrL7ZQuVzjjkL
# MK9VJlXTVkHpuC8K5S4HHTv2AJx6mOdkMJwS4gLlJ7gXrIVpnxG+aIniGDCCBoIw
# ggRqoAMCAQICEDbCsL18Gzrno7PdNsvJdWgwDQYJKoZIhvcNAQEMBQAwgYgxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtKZXJzZXkg
# Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYDVQQDEyVV
# U0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTIxMDMyMjAw
# MDAwMFoXDTM4MDExODIzNTk1OVowVzELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1Nl
# Y3RpZ28gTGltaXRlZDEuMCwGA1UEAxMlU2VjdGlnbyBQdWJsaWMgVGltZSBTdGFt
# cGluZyBSb290IFI0NjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIid
# 2LlFZ50d3ei5JoGaVFTAfEkFm8xaFQ/ZlBBEtEFAgXcUmanU5HYsyAhTXiDQkiUv
# pVdYqZ1uYoZEMgtHES1l1Cc6HaqZzEbOOp6YiTx63ywTon434aXVydmhx7Dx4IBr
# Aou7hNGsKioIBPy5GMN7KmgYmuu4f92sKKjbxqohUSfjk1mJlAjthgF7Hjx4vvyV
# DQGsd5KarLW5d73E3ThobSkob2SL48LpUR/O627pDchxll+bTSv1gASn/hp6IuHJ
# orEu6EopoB1CNFp/+HpTXeNARXUmdRMKbnXWflq+/g36NJXB35ZvxQw6zid61qmr
# lD/IbKJA6COw/8lFSPQwBP1ityZdwuCysCKZ9ZjczMqbUcLFyq6KdOpuzVDR3ZUw
# xDKL1wCAxgL2Mpz7eZbrb/JWXiOcNzDpQsmwGQ6Stw8tTCqPumhLRPb7YkzM8/6N
# nWH3T9ClmcGSF22LEyJYNWCHrQqYubNeKolzqUbCqhSqmr/UdUeb49zYHr7ALL8b
# AJyPDmubNqMtuaobKASBqP84uhqcRY/pjnYd+V5/dcu9ieERjiRKKsxCG1t6tG9o
# j7liwPddXEcYGOUiWLm742st50jGwTzxbMpepmOP1mLnJskvZaN5e45NuzAHteOR
# lsSuDt5t4BBRCJL+5EZnnw0ezntk9R8QJyAkL6/bAgMBAAGjggEWMIIBEjAfBgNV
# HSMEGDAWgBRTeb9aqitKz1SA4dibwJ3ysgNmyzAdBgNVHQ4EFgQU9ndq3T/9ARP/
# FqFsggIv0Ao9FCUwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wEwYD
# VR0lBAwwCgYIKwYBBQUHAwgwEQYDVR0gBAowCDAGBgRVHSAAMFAGA1UdHwRJMEcw
# RaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0
# aWZpY2F0aW9uQXV0aG9yaXR5LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUH
# MAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggIB
# AA6+ZUHtaES45aHF1BGH5Lc7JYzrftrIF5Ht2PFDxKKFOct/awAEWgHQMVHol9ZL
# Syd/pYMbaC0IZ+XBW9xhdkkmUV/KbUOiL7g98M/yzRyqUOZ1/IY7Ay0YbMniIibJ
# rPcgFp73WDnRDKtVutShPSZQZAdtFwXnuiWl8eFARK3PmLqEm9UsVX+55DbVIz33
# Mbhba0HUTEYv3yJ1fwKGxPBsP/MgTECimh7eXomvMm0/GPxX2uhwCcs/YLxDnBdV
# VlxvDjHjO1cuwbOpkiJGHmLXXVNbsdXUC2xBrq9fLrfe8IBsA4hopwsCj8hTuwKX
# JlSTrZcPRVSccP5i9U28gZ7OMzoJGlxZ5384OKm0r568Mo9TYrqzKeKZgFo0fj2/
# 0iHbj55hc20jfxvK3mQi+H7xpbzxZOFGm/yVQkpo+ffv5gdhp+hv1GDsvJOtJinJ
# mgGbBFZIThbqI+MHvAmMmkfb3fTxmSkop2mSJL1Y2x/955S29Gu0gSJIkc3z30vU
# /iXrMpWx2tS7UVfVP+5tKuzGtgkP7d/doqDrLF1u6Ci3TpjAZdeLLlRQZm867eVe
# XED58LXd1Dk6UvaAhvmWYXoiLz4JA5gPBcz7J311uahxCweNxE+xxxR3kT0WKzAS
# o5G/PyDez6NHdIUKBeE3jDPs2ACc6CkJ1Sji4PKWVT0/MYIGLzCCBisCAQEwaDBU
# MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSswKQYDVQQD
# EyJTZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgQ0EgUjM2AhBpyzlIOS826J/R
# oSZmavTeMAkGBSsOAwIaBQCgeDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkG
# CSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEE
# AYI3AgEVMCMGCSqGSIb3DQEJBDEWBBRK7wCdUPEWffW2LAcJTWvBQQV0SzANBgkq
# hkiG9w0BAQEFAASCAgBLf5b/XvZv+uDWIRqDlc4z4vuXBM1szPLzMAwgt2yB7Kec
# 22RzmMgBXbiQmZ+1ENJr2OWSR5HMZYpyjALRSfbm/DqGmOPyA2v0Ch4fObRnDwYz
# x8VKJY89FxNCuP1nZFZoqvi9qqTeJuIYHdOUCZZNpC5Qc4OpiLGn9MCR532frJHu
# VyP8ymssZPKaDJ5Vl49FKkE3BC5R5QLwqbYFyRJHsGyP8htHffBKX1vUTS0A3UVA
# 3CQ5/ldecT3SBNqENVr1AoyBJLmosSIsxhp5ehjmWlGMV6bYfmY3eX0b2S7Te6f9
# S62gv4TX9BpiZC+ZSZ9YV0LIWb6sSX9OS+gcgj/j77AF0CGXI25b/eubFlDps/1e
# P0LmrdUnjAXhvCM/aOe5a4eLycAeUd6an2RCd0Dv/G99RxxA4XtLwyYOIUJkkTKp
# 0VOfJIDTnOImf0GsoVwCnPYGDUiaYhCqMr4GgVi8jU8sezIvb/FgumRGQh/HsCCs
# KcS/qsPFJl34kjSNrrwEfJfZsJ3EtaYFFGs9lRFrjYVsDSdWF47HE38FWN00R+5V
# RWx9aNpEJQQl6VJVJCb5juHAifOoKzUlinFgNqHQieEXpHkM4yK5brbOXih5593D
# q0AM+DTpY/gophSqwe2yeCrE/wHE67meKuSKWqqIgIk77d4TrsKmIdx943iZ2qGC
# AyIwggMeBgkqhkiG9w0BCQYxggMPMIIDCwIBATBpMFUxCzAJBgNVBAYTAkdCMRgw
# FgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxLDAqBgNVBAMTI1NlY3RpZ28gUHVibGlj
# IFRpbWUgU3RhbXBpbmcgQ0EgUjM2AhA6UmoshM5V5h1l/MwS2OmJMA0GCWCGSAFl
# AwQCAgUAoHkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUx
# DxcNMjUwMjA1MTQxNjAxWjA/BgkqhkiG9w0BCQQxMgQw1yaE/ilU1ZUiIsfOjcz7
# 3mF//M01U4hACt9lZBMXxTgTHAGMe6OktVbmUkrjPFwJMA0GCSqGSIb3DQEBAQUA
# BIICAAO+Ffi+o2X7gj6qawIT9/O3SV1A6MLzZyh4BsXsYpn5bk09J7Cp/clRKAd/
# AhXgB2LQ+yTO0U8PKS791NWR0+sCwJ2O3tLuJ7fjgGkmS+lXuUDN39zFs6Rt9aNA
# yJ/1V0xYHFaeToS2zvAWkZsocVPLMo9khmMCZm8+9Sg6PFxHn/4PVZb1k1Fz9ryW
# DljSiG5eD1T93HSPY6FRXu+MQDYpQHISNJpaYEQ1BM3vZr0x1FufL6jXAqZk3ilF
# 3KQkkSejKUjzUux02HcTAMr9pRUWC5tX5LFME1J9BXFXl0YyMWBVYsJER8/mIboA
# 4SBmzrXN7zOKj8tlN7PG3Mc/K7cZXMwa2wmREgf7fgnMdbFKiqmZw3TdgVUr+wGn
# 81VeFjPFgZKvJKpTwNEiKjYKpk+OsNyZO4Yke22YoID5Yaanqv6ULTmZMOPPBdRl
# aofhBupLyYRZfrx3dRQ+gbPbyVJn77qBHTlx8VSk9ooWjIXEidKDjfkUv3ksQMyC
# Py7AUD8FO5vvemnlUhIyuW7z+SLdO+aovEfrFtQwWgRy8EThVPqi3x5S0Fv0vt0o
# 7eXxwF6Q8IYlIW/HgQal6d8ezQ31KjCFAasn58Nc4hM5QWqcc/b+wVXysqC5nJ3n
# ctkydLnpnqLg0vrDtvl+aJl7sCLno640PpZAtTNJJt7M8WKs
# SIG # End signature block