Tenable.Tools.psm1

# Private Function Example - Replace With Your Function
function Add-PrivateFunction {

  [CmdletBinding()]

  Param (
    # Your parameters go here...
  )

  # Your function code goes here...
  Write-Output "Your private function ran!"

}

function Get-TioAgent {
  <#
  .SYNOPSIS
    Get Tenable Agent Information
  .DESCRIPTION
    This function returns information about available Tenable Agents
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Limit
    Maximum number of Agents to return per query
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName = 'ListAll')]

  param(
    [Parameter(Mandatory = $false,
      HelpMessage = 'Base URI for the Tenable Environment, defaults to Tenable Cloud')]
    [ValidateScript({
        $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
        if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
          [System.UriBuilder]$_
        }
      })]
    [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory = $true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory = $false,
      ParameterSetName = 'ById',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Id of Scanner for which to retrieve details')]
    [Alias("Uuid")]
    [string] $Id,

    [Parameter(Mandatory = $false,
      ParameterSetName = 'ListAll',
      ValueFromPipeline = $false,
      ValueFromPipelineByPropertyName = $false,
      HelpMessage = 'Maximum number of entries to return per API Call')]
    [int] $Size = 1000,

    [Parameter(Mandatory = $false,
      ParameterSetName = 'ListAll',
      ValueFromPipeline = $false,
      ValueFromPipelineByPropertyName = $false,
      HelpMessage = 'The page number of the result set')]
    [ValidateRange(0, 10000)]
    [int32] $Page = 0
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose ('{0}: Entering function' -f $Me)

    $Uri.Path = [io.path]::combine($Uri.Path, 'scanners', 'null', 'agents')

    $Method = 'GET'
  }

  Process {
    # Intitialise result set
    $ResCount = 0

    # Parse exisint query parameters
    $QueryParam = [System.Web.HttpUtility]::ParseQueryString($Uri.Query)

    if ($PSBoundParameters.ContainsKey('Id')) {
      # We're looking up a specific Id
      $Uri.Path = [io.path]::combine($Uri.Path, $Id)
    } else {
      # We're not looking for a specific ID, so set limit and offset based on requested page
      $QueryParam['limit'] = $Size
      $QueryParam['offset'] = ($Page * $Size)
    }

    # Add the query back to the URI
    $Uri.Query = $QueryParam.ToString()

    # Make the call to the API
    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $Response = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys -Method $Method

    # Determine if we need to loop through pages
    if (!($PSBoundParameters.ContainsKey('Page'))) {
      # A specific page was not requested, get all available pages
      $Page = 0
      if ($Response.agents) {
        Write-Output $Response.agents

        Write-Debug ('{0}: Response: {1}' -f $Me, ($Response | ConvertTo-Json -Compress -Depth 10))
        $ResCount += $Response.agents.Count
        $TotalResults = $Response.pagination.total

        # Get the number pages of results (have to round up)
        $Pages = [Math]::Ceiling($TotalResults / $Size)
        Write-Verbose ('{0}: Fetching all pages. Total Results: {1}; Total Pages: {2}' -f $Me, $TotalResults, $Pages)

      } else {
        Write-Output $Response
      }

      # We have the first page, loop through the remainder (if there are any)
      for ($Page = 1; $Page -lt $Pages; $Page++ ) {
        Clear-Variable 'Response'
        Write-Verbose ("{0} : Fetching Page {1}" -f $Me, $Page)

        # Call ourselves to get the next page(s)
        $Response = Get-TioAgent -ApiKeys $ApiKeys -Size $Size -Page $Page

        Write-Debug ('{0}: Page: {1}; Response: {2}' -f $Me, $Page, ($Response | ConvertTo-Json -Compress -Depth 10))

        Write-Verbose ("{0} : Fetched Page {1}" -f $Me, $Page)
        Write-Output $Response
        $ResCount += $Response.Count
      }
    } else {
      if ($Response.agents) {
        Write-Output $Response.agents
      }
      else {
        Write-Output $Response
      }
    }

  }

  End {

  }
}

function Get-TioAsset {
  <#
  .SYNOPSIS
    Get Asset information
  .DESCRIPTION
    This function returns information about one or more Tenable.io Assets
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Body
    PSCustomObject containing data to be sent as HTTP Request Body in JSON format.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ListAll')]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "GET",

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ById',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Id (UUID) of Asset for which to retrieve details')]
    [Alias("Id")]
    [string] $Uuid,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ByHostname',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Hostname for which to retrieve details')]
    [string] $Hostname,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ByIpv4',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'IPv4 for which to retrieve details')]
    [string] $IPv4
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "assets")
  }

  Process {
    if ($PSBoundParameters.ContainsKey('Uuid')) {
      # We're looking up a specific Id
      $Uri.Path = [io.path]::combine($Uri.Path, $Uuid)
    }

    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $Assets = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys

    if ($PSBoundParameters.ContainsKey('Hostname')) {
      Write-Verbose ('Checking Assets by Hostname: ' + $Hostname)
      # We're looking up a specific Hostname
      foreach ($Asset in $Assets.assets) {
        Write-Verbose (' Checking: ' + $Folder.name)
        if ($asset.hostname -eq $Hostname) {
          Write-Output $Asset
        }
      }
    } elseif ($PSBoundParameters.ContainsKey('IPv4')) {
      Write-Verbose ('Checking Folders by Type: ' + $IPv4)
      # We're looking up a specific Type
      foreach ($Asset in $Assets.assets) {
        Write-Verbose (' Checking: ' + $Asset.ipv4)
        if ($Asset.ipv4 -eq $Ipv4) {
          Write-Output $Asset
        }
      }
    } else {
      if ($Assets.assets) {
        Write-Output $Assets.assets
      } else {
        Write-Output $Assets
      }
    }
  }

  End {

  }
}

function Get-TioExportAsset {
  <#
  .SYNOPSIS
    Exports all assets that match the request criteria.
  .DESCRIPTION
    This function returns information about one or more Tenable.io Assets
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Filter
    Specifies filters for exported assets. To return all assets, omit the filters object. If
    your request specifies multiple filters, the system combines the filters using the AND search operator.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ByTag')]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "POST",

    [Parameter(Mandatory=$false,
      HelpMessage = 'Results per chunk')]
    [int64] $ChunkSize = 1000,

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ByFilter',
      HelpMessage = 'Filter condition')]
    [psobject] $Filter,

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ByTag',
      HelpMessage = 'Tag Category Filter condition')]
    [string] $TagCategory,

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ByTag',
      HelpMessage = 'Tag Value Filter condition')]
    [string] $TagValue,

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ByUuid',
      HelpMessage = 'Tag Value Filter condition')]
    [string] $Uuid
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $RetryInterval = 30

    $Uri.Path = [io.path]::combine($Uri.Path, "assets/export")

    if (!$PSBoundParameters.ContainsKey('Uuid')) {
      # Starting a new search
      $Body = @{}
      $Body.Add('chunk_size',$ChunkSize)

      if ($PSBoundParameters.ContainsKey('TagCategory')) {
        $Body.Add('filters',@{})
        $Body.filters.add(('tag.' + $TagCategory),$TagValue)
      } elseif ($PSBoundParameters.ContainsKey('Filter')) {
        $Body.Add('filters',$Filter)
      }
    }

  }

  Process {

    if (!$PSBoundParameters.ContainsKey('Uuid')) {
      # Initiate the Asset Export
      Write-Verbose "$Me : Uri : $($Uri.Uri)"
      $ExportParams = @{
        ApiKeys = $ApiKeys
        ChunkSize = $ChunkSize
      }

      if ($PSBoundParameters.ContainsKey('Filter')) {
        $ExportParams.Add('Filter', $Filter)
      }

      if ($PSBoundParameters.ContainsKey('TagCategory')) {
        $ExportParams.Add('TagCategory', $TagCategory)
        $ExportParams.Add('TagValue', $TagValue)
      }

      $AssetExport = Start-TioExportAsset @ExportParams

      Write-Verbose ('{0}: AssetExport: {1}' -f $Me, ($AssetExport | ConvertTo-Json -depth 10 -Compress))

      $Uuid = $AssetExport
    }

    Write-Verbose ($Me + ': Asset Export ID: ' + $Uuid)
    # Start by checking the status
    $ExportStatus = Get-TioExportAssetStatus -ApiKeys $ApiKeys -Uuid $Uuid

    if ($ExportStatus.Error) {
      Write-Error ("$Me : Exception: $($ExportStatus.Code) : $($ExportStatus.Note)")
    }

    Write-Verbose ($Me + ': Asset Export Status: ' +$ExportStatus.status)

    # Wait until the export is finished
    while ($ExportStatus.status -ne 'FINISHED') {
      Start-Sleep -Seconds $RetryInterval

      $ExportStatus = Get-TioExportAssetStatus -ApiKeys $ApiKeys -Uuid $Uuid

      # Check for failures
      if ($ExportStatus.status -eq 'CANCELLED' -or $ExportStatus.status -eq 'ERROR') {
        Write-Error 'Asset Export Failed'
        exit 1
      }

      Write-Verbose ($Me + ': Asset Export Status: ' + $ExportStatus.status)
    }

    # We should have our results available for download now

    $Assets = @()

    foreach ($Chunk in $ExportStatus.chunks_available) {
      $Assets += Get-TioExportAssetChunk -ApiKeys $ApiKeys -Uuid $Uuid -Chunk $Chunk
    }

    Write-Output $Assets

  }

  End {

  }
}

function Get-TioExportAssetChunk {
  <#
  .SYNOPSIS
    Download exported asset chunk by ID.
  .DESCRIPTION
    Download exported asset chunk by ID. Chunks are available for download for up to 24 hours after they have been created.
    Tenable.io returns a 404 message for expired chunks.
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Uuid
    The UUID of the export request.
  .PARAMETER Chunk
    The ID of the Asset Chunk you want to download
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ById')]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "GET",

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ById',
      HelpMessage = 'Asset Export UUID')]
    [string] $Uuid,

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ById',
      HelpMessage = 'Asset Chunk Number')]
    [string] $Chunk
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "assets/export", $uuid, "chunks", $Chunk)

  }

  Process {
    # Initiate the Asset Export
    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $ExportChunk = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys -Method $Method

    Write-Output $ExportChunk

  }

  End {

  }
}

function Get-TioExportAssetStatus {
  <#
  .SYNOPSIS
    Get the status of an in-progress asset export
  .DESCRIPTION
    This function returns information about one or more Tenable.io Assets
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Filter
    Specifies filters for exported assets. To return all assets, omit the filters object. If
    your request specifies multiple filters, the system combines the filters using the AND search operator.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ListAll')]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "GET",

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ById',
      HelpMessage = 'Filter condition')]
    [string] $Uuid
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "assets/export", $uuid, "status")

  }

  Process {
    # Get an updated asset export status
    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $ExportStatus = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys -Method $Method

    if ($ExportStatus.exports) {
      Write-Output $ExportStatus.exports
    } else {
      Write-Output $ExportStatus
    }

  }

  End {

  }
}

function Get-TioExportVuln {
  <#
  .SYNOPSIS
    Exports all vulnerabilities that match the request criteria.
  .DESCRIPTION
    This function returns information about one or more Tenable.io Vulnerabilities
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Filter
    Specifies filters for exported vulnerabilities. To return all vulnerabilities, omit the filters object. If
    your request specifies multiple filters, the system combines the filters using the AND search operator.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='IncludeAll')]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "POST",

    [Parameter(Mandatory=$false,
      HelpMessage = 'Assets per chunk')]
    [int64] $ChunkSize = 1000,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Include Unlicensed Assets')]
    [switch] $IncludeUnlicensed,

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ByFilter',
      HelpMessage = 'Filter condition')]
    [psobject] $Filter,

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ByUuid',
      HelpMessage = 'Tag Value Filter condition')]
    [string] $Uuid
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $RetryInterval = 30

    $Uri.Path = [io.path]::combine($Uri.Path, "vulns/export")

    if (!$PSBoundParameters.ContainsKey('Uuid')) {
      # Starting a new search
      $Body = @{}
      $Body.Add('num_assets',$ChunkSize)

      if ($PSBoundParameters.ContainsKey('TagCategory')) {
        $Body.Add('filters',@{})
        $Body.filters.add(('tag.' + $TagCategory),$TagValue)
      } elseif ($PSBoundParameters.ContainsKey('Filter')) {
        $Body.Add('filters',$Filter)
      }
    }

    if ($PSBoundParameters.ContainsKey('IncludeUnlicensed')) {
      # Include Unlicensed Assets in Vulnerability Export
      $Body.Add('include_unlicensed','true')
    }

  }

  Process {

    if (!$PSBoundParameters.ContainsKey('Uuid')) {
      # Initiate the Vuln Export
      Write-Verbose "$Me : Uri : $($Uri.Uri)"
      $ExportParams = @{
        ApiKeys   = $ApiKeys
        ChunkSize = $ChunkSize
      }

      if ($PSBoundParameters.ContainsKey('Filter')) {
        $ExportParams.Add('Filter', $Filter)
      }

      $VulnExport = Start-TioExportVuln @ExportParams

      $Uuid = $VulnExport
    }

    Write-Verbose ($Me + ': Vuln Export ID: ' + $Uuid)
    # Start by checking the status
    $ExportStatus = Get-TioExportVulnStatus -ApiKeys $ApiKeys -Uuid $Uuid

    if ($ExportStatus.Error) {
      Write-Error ("$Me : Exception: $($ExportStatus.Code) : $($ExportStatus.Note)")
    }

    Write-Verbose ($Me + ': Vuln Export Status: ' +$ExportStatus.status)

    # Wait until the export is finished
    while ($ExportStatus.status -ne 'FINISHED') {
      Start-Sleep -Seconds $RetryInterval

      $ExportStatus = Get-TioExportVulnStatus -ApiKeys $ApiKeys -Uuid $Uuid

      # Check for failures
      if ($ExportStatus.status -eq 'CANCELLED' -or $ExportStatus.status -eq 'ERROR') {
        Write-Error 'Vuln Export Failed'
        exit 1
      }

      Write-Verbose ($Me + ': Vuln Export Status: ' + $ExportStatus.status)
    }

    # We should have our results available for download now

    $Vulns = @()

    foreach ($Chunk in $ExportStatus.chunks_available) {
      $Vulns += Get-TioExportVulnChunk -ApiKeys $ApiKeys -Uuid $Uuid -Chunk $Chunk
    }

    Write-Output $Vulns

  }

  End {

  }
}

function Get-TioExportVulnChunk {
  <#
  .SYNOPSIS
    Download exported Vuln chunk by ID.
  .DESCRIPTION
    Download exported Vuln chunk by ID. Chunks are available for download for up to 24 hours after they have been created.
    Tenable.io returns a 404 message for expired chunks.
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Uuid
    The UUID of the export request.
  .PARAMETER Chunk
    The ID of the Vuln Chunk you want to download
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ById')]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "GET",

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ById',
      HelpMessage = 'Vuln Export UUID')]
    [string] $Uuid,

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ById',
      HelpMessage = 'Vuln Chunk Number')]
    [string] $Chunk
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "vulns/export", $uuid, "chunks", $Chunk)

  }

  Process {
    # Initiate the Vuln Export
    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $ExportChunk = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys -Method $Method

    Write-Output $ExportChunk

  }

  End {

  }
}

function Get-TioExportVulnStatus {
  <#
  .SYNOPSIS
    Get the status of an in-progress Vuln export
  .DESCRIPTION
    This function returns information about one or more Tenable.io Vulns
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Filter
    Specifies filters for exported Vulns. To return all Vulns, omit the filters object. If
    your request specifies multiple filters, the system combines the filters using the AND search operator.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ListAll')]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "GET",

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ById',
      HelpMessage = 'Filter condition')]
    [string] $Uuid
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "vulns/export", $uuid, "status")

  }

  Process {
    # Get an updated Vuln export status
    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $ExportStatus = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys -Method $Method

    if ($ExportStatus.exports) {
      Write-Output $ExportStatus.exports
    } else {
      Write-Output $ExportStatus
    }

  }

  End {

  }
}

function Start-TioExportAsset {
  <#
  .SYNOPSIS
    Starts an asynchronous export of all assets that match the request criteria.
  .DESCRIPTION
    This function returns the UUID of the export job that is executing in the Tenable.io environment
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Filter
    Specifies filters for exported assets. To return all assets, omit the filters object. If
    your request specifies multiple filters, the system combines the filters using the AND search operator.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName = 'ByTag', SupportsShouldProcess)]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "POST",

    [Parameter(Mandatory = $false,
      HelpMessage = 'Results per chunk')]
    [int64] $ChunkSize = 1000,

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ByFilter',
      HelpMessage = 'Filter condition')]
    [PSObject] $Filter,

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ByTag',
      HelpMessage = 'Tag Category Filter condition')]
    [string] $TagCategory,

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ByTag',
      HelpMessage = 'Tag Value Filter condition')]
    [string] $TagValue
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "assets/export")

    # Starting a new search
    $Body = @{}
    $Body.Add('chunk_size',$ChunkSize)

    if ($PSBoundParameters.ContainsKey('TagCategory')) {
      $Body.Add('filters',@{})
      $Body.filters.add(('tag.' + $TagCategory),$TagValue)
    } elseif ($PSBoundParameters.ContainsKey('Filter')) {
      $Body.Add('filters',$Filter)
    }

    Write-Debug ('{0}: Filters: {1}' -f $Me, ($Filter | ConvertTo-Json -Compress))
  }

  Process {

    # Initiate the Asset Export
    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    Write-Debug ('{0}: Body: {1}' -f $Me, ($Body | ConvertTo-Json -Depth 10 -Compress))

    if ($PSCmdlet.ShouldProcess("$Uri", "Start Asset Export Task")) {
      $AssetExport = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys -Method $Method -Body $Body
    }

    $Uuid = $AssetExport.export_uuid

    Write-Verbose ($Me + ': Asset Export ID: ' + $Uuid)

    Write-Output $Uuid

  }

  End {

  }
}

function Start-TioExportVuln {
  <#
  .SYNOPSIS
    Starts an export of all vulnerabilities that match the request criteria.
  .DESCRIPTION
    This function returns the UUID for the export task running in Tenable.io
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Filter
    Specifies filters for exported vulnerabilities. To return all vulnerabilities, omit the filters object. If
    your request specifies multiple filters, the system combines the filters using the AND search operator.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName = 'IncludeAll', SupportsShouldProcess)]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "POST",

    [Parameter(Mandatory=$false,
      HelpMessage = 'Assets per chunk')]
    [int64] $ChunkSize = 1000,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Include Unlicensed Assets')]
    [switch] $IncludeUnlicensed,

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ByFilter',
      HelpMessage = 'Filter condition')]
    [psobject] $Filter
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me


    $Uri.Path = [io.path]::combine($Uri.Path, "vulns/export")

    # Starting a new search
    $Body = @{}
    $Body.Add('num_assets',$ChunkSize)

    if ($PSBoundParameters.ContainsKey('TagCategory')) {
      $Body.Add('filters',@{})
      $Body.filters.add(('tag.' + $TagCategory),$TagValue)
    } elseif ($PSBoundParameters.ContainsKey('Filter')) {
      $Body.Add('filters',$Filter)
    }

    if ($PSBoundParameters.ContainsKey('IncludeUnlicensed')) {
      # Include Unlicensed Assets in Vulnerability Export
      $Body.Add('include_unlicensed','true')
    }

  }

  Process {

    # Initiate the Vuln Export
    Write-Verbose "$Me : Uri : $($Uri.Uri)"

    if ($PSCmdlet.ShouldProcess($Uri, "Start Vulnerability Export Task")) {
      $VulnExport = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys -Method $Method -Body $Body
    }

    $Uuid = $VulnExport.export_uuid

    Write-Verbose ($Me + ': Vuln Export ID: ' + $Uuid)

    Write-Output $Uuid

  }

  End {

  }
}

function Stop-TioExportAsset {
  <#
  .SYNOPSIS
    Cancel an in-progress asset export
  .DESCRIPTION
    This function returns information about one or more Tenable.io Assets
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Filter
    Specifies filters for exported assets. To return all assets, omit the filters object. If
    your request specifies multiple filters, the system combines the filters using the AND search operator.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ListAll',SupportsShouldProcess)]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "POST",

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ById',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Filter condition')]
    [string] $Uuid
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "assets/export", $uuid, "cancel")

  }

  Process {
    # Initiate the Asset Export
    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    if ($PSCmdlet.ShouldProcess($Uri.Uri, "Cancel Asset Export")) {
      $ExportStatus = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys -Method $Method -Body $Filter
    }

    Write-Output $ExportStatus

  }

  End {

  }
}

function Stop-TioExportVuln {
  <#
  .SYNOPSIS
    Cancel an in-progress Vuln export
  .DESCRIPTION
    This function returns information about one or more Tenable.io Vulns
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Filter
    Specifies filters for exported Vulns. To return all Vulns, omit the filters object. If
    your request specifies multiple filters, the system combines the filters using the AND search operator.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ListAll',SupportsShouldProcess)]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "POST",

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ById',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Filter condition')]
    [string] $Uuid
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "vulns/export", $uuid, "cancel")

  }

  Process {
    # Initiate the Vuln Export
    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    if ($PSCmdlet.ShouldProcess($Uri.Uri, "Cancel Vuln Export")) {
      $ExportStatus = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys -Method $Method -Body $Filter
    }

    Write-Output $ExportStatus

  }

  End {

  }
}

function Get-TioFolder {
  <#
  .SYNOPSIS
    Lists both Tenable-provided folders and the current user's custom folders.
  .DESCRIPTION
    This function returns information about one or more Tenable.io Folders
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ListAll')]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "GET",

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ByName',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Name of folder for which to retrieve details')]
    [string] $Name,

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ByType',
      HelpMessage = 'Type of folder for which to retrieve details')]
    [ValidateSet('main', 'trash', 'custom')]
    [string] $Type
    )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "folders")

  }

  Process {
    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $Folders = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys

    Write-Debug ($Folders | ConvertTo-Json -depth 10)

    if ($PSBoundParameters.ContainsKey('Name')) {
      Write-Verbose ('Checking Folders by Name: ' + $Name)
      # We're looking up a specific Name
      foreach ($Folder in $Folders.folders) {
        Write-Verbose (' Checking: ' + $Folder.name)
        if ($Folder.name -eq $Name) {
          Write-Output $Folder
        }
      }
    } elseif ($PSBoundParameters.ContainsKey('Type')) {
      Write-Verbose ('Checking Folders by Type: ' + $Type)
      # We're looking up a specific Type
      foreach ($Folder in $Folders.folders) {
        Write-Verbose (' Checking: ' + $Folder.type)
        if ($Folder.type -eq $Type) {
          Write-Output $Folder
        }
      }
    } else {
      if ($Folders.folders) {
        Write-Output $Folders.folders
      } else {
        Write-Output $Folders
      }
    }


  }

  End {

  }
}


function Invoke-TioApiRequest {
  <#
  .SYNOPSIS
    Invoke the Tenable.io API
  .DESCRIPTION
    This function is intended to be called by other functions for specific resources/interactions
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Body
    PSCustomObject containing data to be sent as HTTP Request Body in JSON format.
  .PARAMETER Depth
    How deep are we going?
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding()]

  param(
    [Parameter(Mandatory=$true,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri,

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "GET",

    [Parameter(Mandatory=$false,
      HelpMessage = 'PsCustomObject containing data that will be sent as the Json Body')]
    [PsCustomObject] $Body,

    [Parameter(Mandatory=$false,
      HelpMessage = 'How deep are we?')]
    [int] $Depth = 0
    )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    if (($Method -eq 'GET') -and $Body) {
      throw "Cannot specify Request Body for Method GET."
    }

    $Header = @{}
    $ApiKey = "accessKey={0}; secretKey={1}" -f $ApiKeys.AccessKey.GetNetworkCredential().Password, $ApiKeys.SecretKey.GetNetworkCredential().Password
    $Header.Add('X-ApiKeys', ($ApiKey))
    $Header.Add('Content-Type', 'application/json')
    $Header.Add('Accept', 'application/json')

  }

  Process {
    # Setup Error Object structure
    $ErrorObject = [PSCustomObject]@{
      Code                  =   $null
      Error                 =   $false
      Type                  =   $null
      Note                  =   $null
      Raw                   =   $_
    }

    $Results = $null

    # Enforce TLSv1.2
    [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12

    # Make the API Call
    if ($Body) {
      # Make the API Call, using the supplied Body. Contents of $Body are the responsibility of the calling code.
      Write-Verbose "$Me : Body supplied"
      Write-Debug ("$Me : Body : " + ($Body | ConvertTo-Json -Depth 20 -Compress))

      try {
        $Results = Invoke-RestMethod -Method $Method -Uri $Uri.Uri -Headers $Header -Body ($Body|ConvertTo-Json -Depth 10) -ResponseHeadersVariable ResponseHeaders
      }
      catch {
        $Exception = $_.Exception
        Write-Verbose "$Me : Exception : $($Exception.Response.StatusCode.value__) : $($Exception.Message)"
        $ErrorObject.Error = $true
        $ErrorObject.Code = $Exception.Response.StatusCode.value__
        $ErrorObject.Note = $Exception.Message
        $ErrorObject.Raw = $Exception
        Write-Debug ($ErrorObject | ConvertTo-Json -Depth 10)

        return $ErrorObject
      }
      Write-Debug ($ResponseHeaders | ConvertTo-Json -Depth 5)
      Write-Debug ($Results | ConvertTo-Json -Depth 10)
    } else {
      # Make the API Call without a body. This is for GET requests, where details of what we want to get is in the URI
      Write-Verbose "$Me : No Body supplied"
      try {
        $Results = Invoke-RestMethod -Method $Method -Uri $Uri.Uri -Headers $Header -ResponseHeadersVariable ResponseHeaders
      }
      catch {
        $Exception = $_.Exception
        Write-Verbose "$Me : Exception : $($Exception.StatusCode)"
        $ErrorObject.Error = $true
        $ErrorObject.Code = $Exception.Response.StatusCode.value__
        $ErrorObject.Note = $Exception.Message
        $ErrorObject.Raw = $Exception
        # Write-Debug ($ErrorObject | ConvertTo-Json -Depth 2)

        Throw "$Me : Encountered error getting response. $($ErrorObject.Code) : $($ErrorObject.Note) from: $RelLink"

        return $ErrorObject
      }
    }
    Write-Debug ($ResponseHeaders | ConvertTo-Json -Depth 5)

    Write-Output $Results
  }

  End {
    Write-Verbose "Returning from Depth: $Depth"
    return
  }
}

function New-TioCredential {
  <#
  .SYNOPSIS
    Build a new Tenable.io credential object
  .DESCRIPTION
    This function is intended to create a PSObject containing 2 PSCredential Objects containing the AccessKey and SecretKey
  .PARAMETER AccessKey
    String containing the Access Key. If not specified, user will be prompted.
  .PARAMETER SecretKey
    String containing the Secret Key. If not specified, user will be prompted.
  .OUTPUTS
    PSCustomObject containing the AccessKey and SecretKey PSCredential Object, containing the keys supplied.
  #>

  [CmdletBinding(SupportsShouldProcess=$true)]
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Tenable.IO Access Key')]
        [string]  $AccessKey,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Tenable.IO Secret Key')]
    [string]  $SecretKey
    )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

  }

  Process {

    $Credential = @{}

    if ($AccessKey) {
      Write-Verbose 'Access Key Supplied'
      $AccessKeyCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'AccessKey', ($AccessKey | ConvertTo-SecureString -AsPlainText -Force)
    } else {
      $AccessKeyCredential = (Get-Credential -UserName 'AccessKey' -Message 'Tenable.IO Access Key')
    }

    $Credential.Add('AccessKey', $AccessKeyCredential)

    if ($SecretKey) {
      Write-Verbose 'Secret Key Supplied'
      $SecretKeyCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'SecretKey', ($SecretKey | ConvertTo-SecureString -AsPlainText -Force)
    } else {
      $SecretKeyCredential = (Get-Credential -UserName 'SecretKey' -Message 'Tenable.IO Secret Key')
    }

    $Credential.Add('SecretKey', $SecretKeyCredential)

    if ($PSCmdlet.ShouldProcess("Tenable.IO Credential", "Generate a new Tenable.IO Credential Object")) {
      Write-Output $Credential
    }
  }

  End {
    return
  }
}

function Get-TioMsspAccount {
  <#
  .SYNOPSIS
    Get Account information
  .DESCRIPTION
    This function returns information about one or more Tenable.io Accounts
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Body
    PSCustomObject containing data to be sent as HTTP Request Body in JSON format.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ListAll')]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "GET",

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ById',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Id (UUID) of Account for which to retrieve details')]
    [Alias("Id")]
    [string] $Uuid,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ByContainerName',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Container Name of account for which to retrieve details')]
    [string] $Container,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ByCustomName',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Custom name of account for which to retrieve details')]
    [string] $Name,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ByCustomName',
      HelpMessage = 'Require an exact match')]
    [switch] $Exact
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "mssp/accounts")
  }

  Process {
    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $Accounts = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys

    if ($PSBoundParameters.ContainsKey('Uuid')) {
      Write-Verbose ('Checking Accounts by Uuid: ' + $Uuid)
      # We're looking up a specific Uuid
      foreach ($Account in $Accounts.accounts) {
        Write-Verbose (' Checking: ' + $Account.uuid)
        if ($Account.uuid -eq $Uuid) {
          Write-Output $Account
        }
      }
    } elseif ($PSBoundParameters.ContainsKey('Container')) {
      Write-Verbose ('Checking Accounts by Container: ' + $Container)
      # We're looking up by Container Name
      foreach ($Account in $Accounts.Accounts) {
        Write-Verbose (' Checking: ' + $Account.container_name)
        if ($Account.container_name -eq $Container) {
          Write-Output $Account
        }
      }
    } elseif ($PSBoundParameters.ContainsKey('Name')) {
      Write-Verbose ('Checking Accounts by Custom Name: ' + $Name)
      # We're looking up by custom name
      foreach ($Account in $Accounts.Accounts) {
        if ($Exact) {
          Write-Verbose (' Checking (exact): ' + $Account.custom_name)
          if ($Account.custom_name -eq $Name) {
            Write-Output $Account
          }
        } else {
          Write-Verbose (' Checking (match): ' + $Account.custom_name)
          if ($Account.custom_name -match $Name) {
            Write-Output $Account
          }
        }
      }
    } else {
      if ($Accounts.accounts) {
        Write-Output $Accounts.accounts
      } else {
        Write-Output $Accounts
      }
    }
  }

  End {

  }
}

function Get-TioMsspLogo {
  <#
  .SYNOPSIS
    Get Logo information
  .DESCRIPTION
    This function returns information about one or more Tenable.io Logos
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Body
    PSCustomObject containing data to be sent as HTTP Request Body in JSON format.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ListAll')]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "GET",

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ById',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Id (UUID) of Logo for which to retrieve details')]
    [Alias("Id")]
    [string] $Uuid,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ByContainerId',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Container Id of Logo for which to retrieve details')]
    [string] $ContainerId,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ByName',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Custom name of Logo for which to retrieve details')]
    [string] $Name,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ByName',
      HelpMessage = 'Require an exact match')]
    [switch] $Exact
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "mssp/logos")
  }

  Process {
    if ($PSBoundParameters.ContainsKey('Uuid')) {
      # We're looking up a specific Id
      $Uri.Path = [io.path]::combine($Uri.Path, $Uuid)
    }

    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $Logos = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys

    if ($PSBoundParameters.ContainsKey('ContainerId')) {
      Write-Verbose ('Checking Logos by ContainerId: ' + $ContainerID)
      # We're looking up by Container Id
      foreach ($Logo in $Logos.Logos) {
        Write-Verbose (' Checking: ' + $Logo.container_uuid)
        if ($Logo.container_uuid -eq $ContainerId) {
          Write-Output $Logo
        }
      }
    } elseif ($PSBoundParameters.ContainsKey('Name')) {
      Write-Verbose ('Checking Logos by Custom Name: ' + $Name)
      # We're looking up by custom name
      foreach ($Logo in $Logos.logos) {
        if ($Exact) {
          Write-Verbose (' Checking (exact): ' + $Logo.name)
          if ($Logo.name -eq $Name) {
            Write-Output $Logo
          }
        } else {
          Write-Verbose (' Checking (match): ' + $Logo.name)
          if ($Logo.name -match $Name) {
            Write-Output $Logo
          }
        }
      }
    } else {
      if ($Logos.logos) {
        Write-Output $Logos.logos
      } else {
        Write-Output $Logos
      }
    }
  }

  End {

  }
}

function Set-TioMsspLogo {
  <#
  .SYNOPSIS
    Get Logo information
  .DESCRIPTION
    This function returns information about one or more Tenable.io Logos
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Body
    PSCustomObject containing data to be sent as HTTP Request Body in JSON format.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ById',SupportsShouldProcess)]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "PUT",

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ById',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Id (UUID) of Logo for which to assign on account(s)')]
    [Alias("Id")]
    [string] $Uuid,

    [Parameter(Mandatory=$true,
      ParameterSetName = 'ById',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Array of Account Ids to assign logo to')]
    [string[]] $Accounts
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "mssp/logos")
  }

  Process {

    Write-Verbose "$Me : Uri : $($Uri.Uri)"

    $Body = @{}
    $Body.Add('logo_uuid',$Uuid)

    if (($Accounts.GetType()).Name -eq 'String') {
      $AccountList = @()
      $AccountList += $Accounts
    } else {
      $AccountList = $Accounts
    }

    $Body.Add('account_uuids',$AccountList)

    if ($PSCmdlet.ShouldProcess("Logo ID $Uuid", "Assign to Accounts")) {
      $Logos = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys -Method $Method -Body $Body
    }

    Write-Output $Logos

  }

  End {

  }
}

function Get-TioScanner {
  <#
  .SYNOPSIS
    Get Tenable Scanner Information
  .DESCRIPTION
    This function returns information about available Tenable Scanners
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName = 'ListAll')]

  param(
    [Parameter(Mandatory = $false,
      HelpMessage = 'Base URI for the Tenable Environment, defaults to Tenable Cloud')]
    [ValidateScript({
        $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
        if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
          [System.UriBuilder]$_
        }
      })]
    [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory = $true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory = $false,
      ParameterSetName = 'ById',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Id of Scanner for which to retrieve details')]
    [Alias("Uuid")]
    [string] $Id
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "scanners")

    $Method = 'GET'
  }

  Process {
    if ($PSBoundParameters.ContainsKey('Id')) {
      # We're looking up a specific Id
      $Uri.Path = [io.path]::combine($Uri.Path, $Id)
    }

    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $Response = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys -Method $Method

    if ($Response.scanners) {
      Write-Output $Response.scanners
    } else {
      Write-Output $Response
    }
  }

  End {

  }
}

function Get-TioServer {
  <#
  .SYNOPSIS
    Get Tenable Server Information
  .DESCRIPTION
    This function returns information about the Tenable Server
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName = 'ListAll')]

  param(
    [Parameter(Mandatory = $false,
      HelpMessage = 'Base URI for the Tenable Environment, defaults to Tenable Cloud')]
    [ValidateScript({
        $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
        if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
          [System.UriBuilder]$_
        }
      })]
    [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory = $true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "server", "properties")

    $Method = 'GET'
  }

  Process {

    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $Response = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys -Method $Method

    Write-Output $Response
  }

  End {

  }
}

function Get-TioServerStatus {
  <#
  .SYNOPSIS
    Get Tenable Server Status
  .DESCRIPTION
    This function returns the operational status of the Tenable Server
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName = 'ListAll')]

  param(
    [Parameter(Mandatory = $false,
      HelpMessage = 'Base URI for the Tenable Environment, defaults to Tenable Cloud')]
    [ValidateScript({
        $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
        if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
          [System.UriBuilder]$_
        }
      })]
    [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory = $true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "server", "status")

    $Method = 'GET'
  }

  Process {

    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $Response = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys -Method $Method

    Write-Output $Response
  }

  End {

  }
}

function Get-TioTagCategory {
  <#
  .SYNOPSIS
    Get Tag Category information
  .DESCRIPTION
    This function returns information about one or more Tenable.io Tag Categories
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Body
    PSCustomObject containing data to be sent as HTTP Request Body in JSON format.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ListAll')]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "GET",

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ById',
      HelpMessage = 'Id (UUID) of Category for which to retrieve details')]
    [string] $Uuid,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ByFilter',
      HelpMessage = 'Filter condition')]
    [string] $Filter,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ByName',
      HelpMessage = 'Get details of Asset Tag Categry by name')]
    [string] $Name
    )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "tags/categories")

    # Uri Parameter based processing
    $UriQuery = [System.Web.HttpUtility]::ParseQueryString([string]$Uri.Query)

    if ($PSBoundParameters.ContainsKey('Filter')) {
      $UriQuery.Add('f',$Filter)
    } elseif ($PSBoundParameters.ContainsKey('Name')) {
      $NameFilter = 'name:eq:{0}' -f $Name
      $UriQuery.Add('f',$NameFilter)
    }

    # Add the parameters to the URI object
    $Uri.Query = $UriQuery.ToString()
  }

  Process {
    if ($PSBoundParameters.ContainsKey('Uuid')) {
      # We're looking up a specific Id
      $Uri.Path = [io.path]::combine($Uri.Path, $Uuid)
    }

    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $Category = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys

    if ($Category.categories) {
      Write-Output $Category.categories
    } else {
      Write-Output $Category
    }
  }

  End {

  }
}

function Get-TioTagValue {
  <#
  .SYNOPSIS
    Get Tag Category information
  .DESCRIPTION
    This function returns information about one or more Tenable.io Tag Categories
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Body
    PSCustomObject containing data to be sent as HTTP Request Body in JSON format.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ListAll')]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "GET",

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ById',
      HelpMessage = 'Id (UUID) of Tag Category Value for which to retrieve details')]
    [string] $Uuid,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ByFilter',
      HelpMessage = 'Filter condition')]
    [string] $Filter,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ByValue',
      HelpMessage = 'Get details of Asset Tag by Value')]
    [string] $Value,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ByCategoryName',
      HelpMessage = 'Get details of Asset Tag Vaues by Categry name')]
    [string] $CategoryName
    )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "tags/values")

    # Uri Parameter based processing
    $UriQuery = [System.Web.HttpUtility]::ParseQueryString([string]$Uri.Query)

    if ($PSBoundParameters.ContainsKey('Filter')) {
      $UriQuery.Add('f',$Filter)
    } elseif ($PSBoundParameters.ContainsKey('Value')) {
      $TagFilter = 'value:eq:{0}' -f $Value
      $UriQuery.Add('f',$TagFilter)
    } elseif ($PSBoundParameters.ContainsKey('CategoryName')) {
      $TagFilter = 'category_name:eq:{0}' -f $CategoryName
      $UriQuery.Add('f',$TagFilter)
    }

    # Add the parameters to the URI object
    $Uri.Query = $UriQuery.ToString()
  }

  Process {
    if ($PSBoundParameters.ContainsKey('Uuid')) {
      # We're looking up a specific Id
      $Uri.Path = [io.path]::combine($Uri.Path, $Uuid)
    }

    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $Category = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys

    if ($Category.values) {
      Write-Output $Category.values
    } else {
      Write-Output $Category
    }
  }

  End {

  }
}

function Get-TioVulnPlugin {
  <#
  .SYNOPSIS
    Get Vuln Plugin information
  .DESCRIPTION
    This function returns information about one or more Tenable.io Vuln Plugins
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Body
    PSCustomObject containing data to be sent as HTTP Request Body in JSON format.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ListAll')]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "GET",

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ById',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Id of Vuln Plugin for which to retrieve details')]
    [string] $Id,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ListAll',
      ValueFromPipeline = $false,
      ValueFromPipelineByPropertyName = $false,
      HelpMessage = 'The number of records to include in the result set')]
    [ValidateRange(1, 10000)]
    [int32] $Size = 1000,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ListAll',
      ValueFromPipeline = $false,
      ValueFromPipelineByPropertyName = $false,
      HelpMessage = 'The page number of the result set')]
    [ValidateRange(1, 10000)]
    [int32] $Page = 1,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ListAll',
      ValueFromPipeline = $false,
      ValueFromPipelineByPropertyName = $false,
      HelpMessage = 'Only provide Plugins updated since the provided date (YYY-MM-DD)')]
    [string] $LastUpdated

  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "plugins/plugin")
  }

  Process {
    $QueryParam = [System.Web.HttpUtility]::ParseQueryString($Uri.Query)
    if ($PSBoundParameters.ContainsKey('Id')) {
      # We're looking up a specific Id
      $Uri.Path = [io.path]::combine($Uri.Path, $Id)
    } else {
      $QueryParam['size'] = $Size
      $QueryParam['page'] = $Page
      if ($PSBoundParameters.ContainsKey('LastUpdated')) {
        $QueryParam['last_updated'] = $LastUpdated
      }
    }

    $Uri.Query = $QueryParam.ToString()

    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $Plugins = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys

    if (!($PSBoundParameters.ContainsKey('Page'))) {
      # A specific page was not requested, get all available pages
      $Page = 1
      if ($Plugins.data.plugin_details) {
        Write-Output $Plugins.data.plugin_details
        $ResCount = $Plugins.data.plugin_details.Count
      } else {
        Write-Output $Plugins
      }
      While ($ResCount -eq $size) {
        $Page++
        Write-Verbose ("{0} : Fetching Page {1}" -f $Me, $Page)
        $Plugins = Get-TioVulnPlugin -ApiKeys $ApiKeys -Method $Method -Size $Size -Page $Page
        Write-Output $Plugins
        $ResCount = $Plugins.Count
      }
    } else {
      # Provide only the requested page
      if ($Plugins.data.plugin_details) {
        Write-Output $Plugins.data.plugin_details
      } else {
        Write-Output $Plugins
      }
    }
  }

  End {

  }
}

function Get-TioVulnPluginFamily {
  <#
  .SYNOPSIS
    Get Vuln Plugin information
  .DESCRIPTION
    This function returns information about one or more Tenable.io Vuln Plugins
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .PARAMETER Method
    Valid HTTP Method to use: GET (Default), POST, DELETE, PUT
  .PARAMETER Body
    PSCustomObject containing data to be sent as HTTP Request Body in JSON format.
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName='ListAll')]

  param(
    [Parameter(Mandatory=$false,
      HelpMessage = 'Full URI to requested resource, including URI parameters')]
    [ValidateScript({
      $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
      if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
        [System.UriBuilder]$_
      }
    })]
        [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory=$true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory=$false,
      HelpMessage = 'Method to use when making the request. Defaults to GET')]
    [ValidateSet("Post","Get","Put","Delete")]
    [string] $Method = "GET",

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ById',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'The ID of the plugin family you want to retrieve the list of plugins for.')]
    [string] $Id,

    [Parameter(Mandatory=$false,
      ParameterSetName = 'ListAll',
      ValueFromPipeline = $false,
      ValueFromPipelineByPropertyName = $false,
      HelpMessage = 'Specifies whether to return all plugin families. If true, the plugin families hidden in Tenable Vulnerability Management UI, for example, Port Scanners, are included in the list.')]
    [boolean] $All = $false

  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "plugins/families")
  }

  Process {
    if ($PSBoundParameters.ContainsKey('Id')) {
      # We're looking up a specific Id
      $Uri.Path = [io.path]::combine($Uri.Path, $Id)
    }

    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $PluginFamilies = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys

    if ($PluginFamilies.families) {
      Write-Output $PluginFamilies.families
      $ResCount = $PluginFamilies.families.Count
    } elseif ($PluginFamilies.plugins) {
      Write-Output $PluginFamilies.plugins
      $ResCount = $PluginFamilies.plugins.count
    } else {
      Write-Output $PluginFamilies
    }
    While ($ResCount -eq $size) {
      $Page++
      Write-Verbose ("{0} : Fetching Page {1}" -f $Me, $Page)
      $PluginFamilies = Get-TioVulnPlugin -ApiKeys $ApiKeys -Method $Method -Size $Size -Page $Page
      Write-Output $PluginFamilies
      $ResCount = $PluginFamilies.Count
    }
  }

  End {

  }
}

function Get-TioVulnScan {
  <#
  .SYNOPSIS
    Get Tenable Scan Information
  .DESCRIPTION
    This function returns information about configured Tenable Vulnerability Scans
  .PARAMETER Uri
    Base API URL for the API Call
  .PARAMETER ApiKeys
    PSObject containing PSCredential Objects with AccessKey and SecretKey.
    Must contain PSCredential Objects named AccessKey and SecretKey with the respective keys stored in the Password property
  .OUTPUTS
    PSCustomObject containing results if successful. May be $null if no data is returned
    ErrorObject containing details of error if one is encountered.
  #>

  [CmdletBinding(DefaultParameterSetName = 'ListAll')]

  param(
    [Parameter(Mandatory = $false,
      HelpMessage = 'Base URI for the Tenable Environment, defaults to Tenable Cloud')]
    [ValidateScript({
        $TypeName = $_ | Get-Member | Select-Object -ExpandProperty TypeName -Unique
        if ($TypeName -eq 'System.String' -or $TypeName -eq 'System.UriBuilder') {
          [System.UriBuilder]$_
        }
      })]
    [System.UriBuilder]  $Uri = 'https://cloud.tenable.com',

    [Parameter(Mandatory = $true,
      HelpMessage = 'PSObject containing PSCredential Objects with AccessKey and SecretKey')]
    [PSObject]  $ApiKeys,

    [Parameter(Mandatory = $false,
      ParameterSetName = 'ById',
      ValueFromPipeline = $true,
      ValueFromPipelineByPropertyName = $true,
      HelpMessage = 'Id of Scanner for which to retrieve details')]
    [Alias("Uuid")]
    [string] $Id
  )

  Begin {
    $Me = $MyInvocation.MyCommand.Name

    Write-Verbose $Me

    $Uri.Path = [io.path]::combine($Uri.Path, "scans")

    $Method = 'GET'
  }

  Process {
    if ($PSBoundParameters.ContainsKey('Id')) {
      # We're looking up a specific Id
      $Uri.Path = [io.path]::combine($Uri.Path, $Id)
    }

    Write-Verbose "$Me : Uri : $($Uri.Uri)"
    $Response = Invoke-TioApiRequest -Uri $Uri -ApiKeys $ApiKeys -Method $Method

    if ($Response.scans) {
      Write-Output $Response.scans
    } else {
      Write-Output $Response
    }
  }

  End {

  }
}

Export-ModuleMember -Function Get-TioAgent, Get-TioAsset, Get-TioExportAsset, Get-TioExportAssetChunk, Get-TioExportAssetStatus, Get-TioExportVuln, Get-TioExportVulnChunk, Get-TioExportVulnStatus, Start-TioExportAsset, Start-TioExportVuln, Stop-TioExportAsset, Stop-TioExportVuln, Get-TioFolder, Invoke-TioApiRequest, New-TioCredential, Get-TioMsspAccount, Get-TioMsspLogo, Set-TioMsspLogo, Get-TioScanner, Get-TioServer, Get-TioServerStatus, Get-TioTagCategory, Get-TioTagValue, Get-TioVulnPlugin, Get-TioVulnPluginFamily, Get-TioVulnScan

# SIG # Begin signature block
# MIIt4gYJKoZIhvcNAQcCoIIt0zCCLc8CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCy1kxIb80GIdjG
# HzP4CduJI0w1oJz4Ag1VOwDR1VrxIaCCEyswggWQMIIDeKADAgECAhAFmxtXno4h
# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
# eE4wggbfMIIEx6ADAgECAhABYmy7Lnkq4ZrwtGPYmCG8MA0GCSqGSIb3DQEBCwUA
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
# ODQgMjAyMSBDQTEwHhcNMjQwMTE1MDAwMDAwWhcNMjUwMTE0MjM1OTU5WjBnMQsw
# CQYDVQQGEwJBVTERMA8GA1UECBMIVmljdG9yaWExFTATBgNVBAcTDE5vdHRpbmcg
# SGlsbDEWMBQGA1UEChMNSVBTZWMgUHR5IEx0ZDEWMBQGA1UEAxMNSVBTZWMgUHR5
# IEx0ZDCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALbFLv+X/DB/f3wO
# 2nvk53ZIkPhN7tzZCRKUDqY+u41Eb+QPgM/KhDP7Kl8g7uoapmrpyIPNoydJghST
# C0rv23fAeOIVnA1IBFPCx4r/cgcEKKLD4QFxPhLS6pFWCEbHHWB6IXjt5uTM3/5f
# c7qLunpnHKB4Nfh0845UzA30sz9mnT6CVpVKVl9owUNJusOXOtZCNrrG7nXXOphi
# YtinoVCFQmq8LJO2wPQUTMY91W9IN7lPW4SARDkDidAovZu6BCveL4+K3K7UsIBT
# pPopOLt6JXcitfedRsDUxz8v+Gak66dgCu2ie0jKC043FLr8ADZkHLcDKbHJnpQO
# OYrOls50NU3ATuNKABUjkrtYy63WdMbDGYeCNbKSESUIkkhzG02dswRnBk0CN65V
# +Jsyts6D3Ag6x10qeambGDEH8nlIZY2wdTvqT5UpGg6GvrOwu2cSt8eWYn69LCtB
# XJCP6Aackgpbm8LbXHs2EHfg0yMMsdKWHTTfEZYs650U3xQUBwIDAQABo4ICAzCC
# Af8wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFIEj
# lb9LKiYKWjk5Yyx4RPBn1Z6rMD4GA1UdIAQ3MDUwMwYGZ4EMAQQBMCkwJwYIKwYB
# BQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAOBgNVHQ8BAf8EBAMC
# B4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0
# cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25p
# bmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRp
# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI
# QTM4NDIwMjFDQTEuY3JsMIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcwAYYY
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8vY2Fj
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JT
# QTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUA
# A4ICAQB6zn4EhgUEk3+8Oa2clKNbDWIOooLawaw7gO3lJp3PNCN/Ov4BrQ5NqnyO
# GfEK7/eJ/7xGa6+jk4cexYEhFfzAyIUwo+Hm8f/Ui3sClnLBoX0kUBYVKT/+Npj4
# kc+4VncXdDC6q8NY0mTpRX0CdbZZgCwd04d83YX7YwqVsjkMtPCynFaGGYufG5Yo
# V6gyeQy8tnm8maKRlP2yBPIH83gUT2rbfTnUaJ1lpKQP044HWNx75PpwtRK+nq9U
# loYir5A6lAuyVcrTWKrpMxs5bXYxCSphqR+LKCbAa7Gg3P4X5/bTIyrPc7Tv1PsG
# T8yo8wTzvAaZLpl7ncmCrlaiXusbKoVxVTJ8FKUUMLCAelOKKgkdHeUvd3EIuFqM
# cP6sg3cR6wu+hexS7ZOZtkfzguHtyp4A+1a6HhEbTubzY9N9Bom0WNWyUe3Yzd5d
# FaqtIkntwju48TIHdvP06Kh2166c7mIX+Krq/TR8AeND2FhhsNO/yTP+aU6CebBF
# M94Ds14Q9aNPYD0ml0lPn2nFIcGPZetfnXCxQC/eudLrGd8jGaQjeeKQIvkhiCqL
# POXcTQXu2PhwU14tJYehWtYZITLScPzLklMIOuGwO3LqsS/vvVXK5+N7CqxHgzAV
# JAjIbgLvBdteW/zoUiJLifJNOdSAVc4DvDdw4elMWpIR7gCzfDGCGg0wghoJAgEB
# MH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYD
# VQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNI
# QTM4NCAyMDIxIENBMQIQAWJsuy55KuGa8LRj2JghvDANBglghkgBZQMEAgEFAKCB
# hDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEE
# AYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJ
# BDEiBCBQ9STNuGuZV8ah+kUN2X97J1wQcxAg1ATin7cCorzf7jANBgkqhkiG9w0B
# AQEFAASCAYCS49Z7dXhopzk3LIcJ4jopm//AmskWEMn6vV0Kk+E4SWIYFOPd/QQE
# AU38mKH7/aBHzCwiEZMcfdx+SxTF8iUYhEO7LLPgzzXmeLI3LJLdv/HbytiBcZYh
# FkotCf2Oy5TgEyAkrBPgAqUm7SGBjvBwKVwsCX1lTz5OeRJ5ehQZoUAvEkU8WjUU
# DSOmWH2fYXj71nKDjtUN9pQFN60gpKFkMC0dFdA0UQF9FNfnIZmXqoc+nBSoHjhk
# wPkPUHzRTKGIhKFmEn5MXIrJhy+CvwQOy53AAD7+gsznCkjSKIJgkn+KORYXggJt
# MHRLNcM5R4WdBJw6u5sUd1b+UNpBjSa9bvOhD6SA66Qa73LJeylnqjUaawmdFBUX
# CFKqsmXBWVOKnbz0RggV5XeQcwn3bCp8YXFLmFvYxs3OdjRztxNJCHjaK5szKs0d
# gbpoGAi8o8ILJZuNDcLanBwqvx4BH7yT5agxB7nuM91SQMgZm9E5OxYQWomaGPze
# W9qmoBuP/iihghdaMIIXVgYKKwYBBAGCNwMDATGCF0YwghdCBgkqhkiG9w0BBwKg
# ghczMIIXLwIBAzEPMA0GCWCGSAFlAwQCAgUAMIGHBgsqhkiG9w0BCRABBKB4BHYw
# dAIBAQYJYIZIAYb9bAcBMEEwDQYJYIZIAWUDBAICBQAEMDhtK800Gerq0apxwD1Q
# qE/0QmlALamSWb8ecpHWiJ4yw++6LuJmpiF1+mHqM8HLdwIQPz1V1LWn+XQCAFYy
# yKsK8xgPMjAyNDEwMjQwMjQ1MzhaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+V
# h8b56QTjMwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoT
# DkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJT
# QTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0z
# NTExMjUyMzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEg
# MB4GA1UEAxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9
# CjAgQxK+CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0
# djvKKO+hDu6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h
# 4eN6zh926SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wx
# GE1/UXjWfISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0e
# V+pIF496OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4r
# A/FE1Q0rqViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3r
# OHNeiYnY+V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDb
# PgjvwmnAalNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCai
# JnfdzUcb3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb
# 2u29Vwgfta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl
# +QIDAQABo4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYD
# VR0lAQH/BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZI
# AYb9bAcBMB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQW
# BBSfVywDdw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8v
# Y3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2
# VGltZVN0YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcw
# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8v
# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hB
# MjU2VGltZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb
# 4ziEEkfZQ5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZm
# Xa5VgW9B76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83p
# XnWwwsxc1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/
# VBQC9VK7iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8V
# lJt1qQcl7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN
# 9FQBhLLISZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwer
# wJZP/Gtbu3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOh
# tloDRWGoCwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaR
# QuR+0BGOzISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YC
# sdhIBHXqBzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKi
# bMs/yXHhDXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzY
# R6z9KGYqXlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoT
# DERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UE
# AxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3
# MDMyMjIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2
# IFRpbWVTdGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# AMaGNQZJs8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjz
# aPp985yJC3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3E
# F3+rGSs+QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYnc
# fGpXevA3eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8O
# pWNs5KbFHc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROp
# VymWJy71h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4i
# FNmCKseSv6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmif
# tkaznTqj1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0
# UfM2SU2LINIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9Ne
# S3YSUZPJjAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCj
# WAkBKAAOhFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTAS
# BgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57I
# bzAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMC
# AYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUF
# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6
# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0
# MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdp
# Q2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCG
# SAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAY
# LhBNE88wU86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQx
# Z822EpZvxFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf
# 7WC2qk+RZp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDV
# inF2ZdrM8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7
# +6adcq/Ex8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJ
# D5TNOXrd/yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvk
# OHOrpgFPvT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJG
# nXUsHicsJttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimG
# sJigK+2VQbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38A
# C+R2AibZ8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d
# 2zc4GqEr9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqG
# SIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFz
# c3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTla
# MGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT
# EHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9v
# dCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8
# MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauy
# efLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34Lz
# B4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+x
# embud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhA
# kHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1Lyu
# GwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2
# PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37A
# lLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD7
# 6GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/
# ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXA
# j6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTAD
# AQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF
# 66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEE
# bTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYB
# BQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3Vy
# ZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAI
# MAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979X
# B72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4k
# vFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU
# 53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pc
# VIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5v
# Iy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggOG
# MIIDggIBATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5j
# LjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBU
# aW1lU3RhbXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAICBQCg
# geEwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0y
# NDEwMjQwMjQ1MzhaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2
# cUhQhyTVhltFMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu
# 1lZ1/tdz2wXWZbkFk5hDj5rbMD8GCSqGSIb3DQEJBDEyBDBmgiK1neoxhcCPA+K1
# w/DAYEnUVZjiwDUYgdNap9YPLhw54cK31gsFXAh0VJsrTd0wDQYJKoZIhvcNAQEB
# BQAEggIAGwquJ9HF9ZCcDmBYXf1qa34aEaRBJu3Hb7tMejEEvJapdA7j7KbCALy2
# bL7iSBK/++sUkNAOb+Dzj7rl/J3jgZrxtndKSJJ4E30weYW0q7pXCRCRxlXFujjD
# DybyCgBC8NTaB3pBPUrYByp2Jbp6pbf1Cc3yrBZxl7xDYwh3yA/ZG7bJRMWwwOE1
# vl8undFoQfjYz3WGhrE1t4BAHDkmHmT1i6nBflC1g4pwrYATODEsRyL2igDDTcq0
# Zmn8O2P0RY85WtCBHFrLvYWonA5usT/2Hx4KyQylh6KQUA49xQhUMqDpXJatawyP
# pU2wMQ/tteircMuu1EfWrTw08P48PgvHRPcMjiw+sH1UsAzhpd4Rh8SRLy+qp0j1
# gWh+f1xkLyhfhDB90yDhaMiPc5YwPzop+yan0K1yv06hr9AFaM0DlOq7908pHJf4
# u0D9iv+4uqEjXfG2MUbh02TVeaQ1qORJVBwpqZC+mJUAN6bASPTaYZVkREF98HOc
# Cv9g9MXl+bRkTvucR7j5Zthi4vUrlJLy8xcu+nc+raBbTYwTyN0CmBtxmUlJpvBT
# Wv07BcVH5/ATn+WohDxoOCSI483IMvd4QfEk2OilYefZTiT5MRrvk8JMB1ofwzyx
# vUK0uS6K0Fhi4SUTiIfCHE+XZxg2u1QBc91qyc21h8D9wW3kRjY=
# SIG # End signature block