scripts/helpers.ps1

# HPE Nimble PowerShell Toolkit.
# File: helpers.ps1
# Description: This file contains common helper routines. These functions are called by generated SDK Cmdlet functions.
#
# © Copyright 2017 Hewlett Packard Enterprise Development LP.
function Connect-NSGroup {
<#
.SYNOPSIS
    Connects to a Nimble Storage group.
.DESCRIPTION
    Connect-NSGroup is an advanced function that provides the initial connection to a Nimble Storage
    array so that other subsequent commands can be run without having to authenticate individually.
    It is recommended to ignore the server certificate validation (-IgnoreServerCertificate param)
    since Nimble uses an untrusted SSL certificate.
.PARAMETER Group
    The DNS name or IP address of the Nimble group.
.PARAMETER Credential
    Specifies a user account that has permission to perform this action. Type a user name, such as User01
    or enter a PSCredential object, such as one generated by the Get-Credential cmdlet. If you type a
    user name, this function prompts you for a password.
.PARAMETER IgnoreServerCertificate
    Ignore the server SSL certificate.
.EXAMPLE
     Connect-NSGroup -Group nimblegroup.yourdns.local -Credential admin -IgnoreServerCertificate
 
     *Note: IgnoreServerCertificate parameter is not available with PowerShell Core
.EXAMPLE
     Connect-NSGroup -Group 192.168.1.50 -Credential admin -IgnoreServerCertificate
 
     *Note: IgnoreServerCertificate parameter is not available with PowerShell Core
.EXAMPLE
     Connect-NSGroup -Group nimblegroup.yourdns.local -Credential admin -ImportServerCertificate
.EXAMPLE
     Connect-NSGroup -Group 192.168.1.50 -Credential admin -ImportServerCertificate
.INPUTS
    None
.OUTPUTS
    None
.NOTES
 
#>

    [cmdletbinding(DefaultParameterSetName='IgnoreServerCertificate')]
    param   (
        [Parameter(Mandatory,position=0)]
        [string]$Group,

        [Parameter(Mandatory,position=1)]
        $Credential=$null,
        

        [Parameter(ParameterSetName='ImportServerCertificate')]
        [switch]$ImportServerCertificate
    
    )

    DynamicParam {

            if ($PSEdition -ne 'Core'){ 

                 $IgnoreServerCertificateAttribute = New-Object System.Management.Automation.ParameterAttribute
                 $IgnoreServerCertificateAttribute.Mandatory = $false
                 #$IgnoreServerCertificateAttribute.Position = 3
                 $IgnoreServerCertificateAttribute.ParameterSetName = 'IgnoreServerCertificate'
                 $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
                 $attributeCollection.Add($IgnoreServerCertificateAttribute)  
                 $IgnoreServerCertificateParam = New-Object System.Management.Automation.RuntimeDefinedParameter('IgnoreServerCertificate', [Switch],$attributeCollection)
                 $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
                 $paramDictionary.Add('IgnoreServerCertificate', $IgnoreServerCertificateParam)
                 return $paramDictionary
                 }
            }

    Process{

        if ($Credential -is [String]) {
            $Credential = Get-Credential $Credential
         }
         $global:Group=$Group
        if ($PSBoundParameters.IgnoreServerCertificate) { $Global:NimbleStorageIgnoreServerCertificate = $true; IgnoreServerCertificate}

        else 
            {
                 $Global:NimbleStorageIgnoreServerCertificate = $false
                 $Global:GlobalImportServerCertificate = $ImportServerCertificate
                 ValidateServerCertificate $group
            }

        Import-LocalizedData -BaseDirectory (Split-Path $PSScriptRoot -parent) -FileName "HPENimblePowerShellToolkit.psd1" -BindingVariable "ModuleData"
        $PSTKVersion = $ModuleData.moduleversion
        $Global:NimbleAppName = "HPENimblePowerShellToolkitV" + $PSTKVersion
        $Global:NimbleStoragePort = 5392
        $Global:BaseUri = "https://$($global:Group):$($NimbleStoragePort)"
        try{
            $Global:NimbleStorageTokenData = Invoke-RestMethod -Uri "$BaseUri/v1/tokens" -Method Post -Body ((@{data = @{username = $Credential.UserName;password = $Credential.GetNetworkCredential().password;app_name = $NimbleAppName}}) | ConvertTo-Json)
            Write-Host "Successfully connected to array $group `n`n"
        }
        catch{
            Write-error "Failed to connect with array $group `n`n $_.Exception.Message" -ErrorAction Stop
        }
        $Global:RestVersion = (Invoke-RestMethod -Uri "$BaseUri/versions").data.name
        $Global:NimbleStorageSession_token = $NimbleStorageTokenData.data.session_token
        $Global:NimbleStorageArray = $group
        $Global:NimbleStorageCommonPSParams=@('Debug','Verbose','ErrorAction','ErrorVariable','InformationAction','InformationVariable','OutBuffer','OutVariable','PipelineVariable','Verbose','WarningAction','WarningVariable','WhatIf','Confim','ItemType')
    }
}

function Disconnect-NSGroup {
<#
.SYNOPSIS
    Disconnects from a Nimble SAN.
.DESCRIPTION
    Disconnect-NSGroup is an advanced function that disconnects the established connection with Nimble Storage
    array.
.EXAMPLE
    Disconnect-NSGroup
.INPUTS
    None
.OUTPUTS
    None
.NOTES
 
#>

    [CmdletBinding()]
    param ( )
    if (Get-Variable NimbleStorageTokenData -Scope Global -ErrorAction SilentlyContinue)
    {
        Remove-NSToken -id $NimbleStorageTokenData.data.id
        remove-Variable -Scope "Global" NimbleStoragePort
        Remove-Variable -Scope "Global" BaseUri
        Remove-Variable -Scope "Global" NimbleStorageTokenData
        Remove-Variable -Scope "Global" RestVersion
        Remove-Variable -Scope "Global" NimbleStorageSession_token
        Remove-Variable -Scope "Global" NimbleStorageArray
        Remove-Variable -Scope "Global" NimbleStorageCommonPSParams
    }
}

function IgnoreServerCertificate {

    [CmdletBinding()]
    param()
    <#
  [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
  [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
 
  add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
 
    public class IDontCarePolicy : ICertificatePolicy {
      public IDontCarePolicy() {}
      public bool CheckValidationResult(
        ServicePoint sPoint, X509Certificate cert,
        WebRequest wRequest, int certProb) {
        return true;
      }
    }
"@
 
  [System.Net.ServicePointManager]::CertificatePolicy = new-object IDontCarePolicy
    write-verbose 'Server certificate ignored'
    #>

     if (-not ([System.Management.Automation.PSTypeName]'CustomCertificateValidationCallback').Type)
    {

      add-type @"
      using System.Net;
    using System.Net.Security;
    using System.Security.Cryptography.X509Certificates;
 
    public static class CustomCertificateValidationCallback {
        public static void Install()
        {
            ServicePointManager.ServerCertificateValidationCallback += CustomCertificateValidationCallback.CheckValidationResult;
        }
 
 
        public static bool CheckValidationResult(
            object sender,
            X509Certificate certificate,
            X509Chain chain,
            SslPolicyErrors sslPolicyErrors)
        {
            // please don't do this. do some real validation with explicit exceptions.
            return true;
        }
    }
"@

    }

    [CustomCertificateValidationCallback]::Install()
}

function Invoke-NimbleStorageRestAPI ()
{
    param(
        [Parameter(Mandatory=$true)][string] $ResourcePath,
        [string] $APIVersion = 'v1',
        [string] $Method = 'GET',
        [hashtable] $RequestParams = @{}
    )

    if (!(Get-Variable NimbleStorageTokenData -Scope Global -ErrorAction SilentlyContinue)) {
       Write-Error -Message "Authentication Info missing. Please use Connect-NSGroup to login." -ErrorAction Stop
    }

    if ( $NimbleStorageIgnoreServerCertificate ) { IgnoreServerCertificate }

    # Form the parameters to Invoke-RestMethod call.
    $WebRequestParams = @{
        Uri = "$BaseUri/$APIVersion/$ResourcePath"
        Header = @{'X-Auth-Token' = $NimbleStorageSession_token}
        Method = $Method
    }

    # Copy request params to different variable. We may need to specifically process few of them.
    $RequestData = @{}
    foreach ($key in $RequestParams.keys)
    {
        # PowerShell serializes Booleans in JSON as True/False. We need all lowercase for Nimble Array's REST Server.
        if ($RequestParams.$key.getType() -eq [bool])
        {
            $RequestData.Add($key, $RequestParams.$key.ToString().ToLower())
        }
        elseif ($key -eq 'fields' -and $Method -eq 'GET')
        {
            # Array of fields. Convert to comma separated list.
            $RequestData.Add('fields', ($RequestParams['fields'] | Select-Object -unique) -join ',')
        }
        else
        {
            $RequestData.Add($key, $RequestParams.$key);
        }
    }

    switch($Method) {
        'GET' {
            # Hashmap supplied in Body for GET request gets converted to query params automatically.
            $WebRequestParams.Add('Body',$RequestData)
        }

        'POST' {
            # Encapsulate request payload in 'data'..
            $RequestDataForNimbleAPI = @{
                data = $RequestData
            }

            $WebRequestParams.Add('Body',($RequestDataForNimbleAPI | ConvertTo-Json -Depth 10))
        }

        'PUT'{
            $RequestDataForNimbleAPI = @{
                data = $RequestData
            }

            $WebRequestParams.Add('Body',($RequestDataForNimbleAPI | ConvertTo-Json -Depth 10))
        }

        'DELETE' {
            # Do nothing. No Body expected/required for delete request.
        }
    }

    Write-Verbose ($WebRequestParams | ConvertTo-Json -Depth 50)
    $max_retry_count = 5
    $retry_count = 0

    do {
        try
            {
          
              if ($retry_count -ne 0)
              {
                Start-Sleep -Milliseconds 30
              }
              $JsonResponse = (Invoke-RestMethod @WebRequestParams  | ConvertTo-Json -Depth 50)
              Write-Verbose "Server Response: $JsonResponse"

              #
              # The Invoke-Restmethod was successful we should exit the retry loop.
              # To do that we will force the retry count max surpass the max.
              #
              $retry_count  = $max_retry_count  + 1
            }
        catch
        {
            if ($_.Exception.Response -ne $null) 
            {
               APIExceptionHandler
            }
            else
            {     
                # if the Error response buffer is null then we will go for retries.
                # if we exhaust the retries we will thrown and error.
                $retry_count = $retry_count + 1
                
                if ($retry_count -gt $max_retry_count)
                {
                    Write-Verbose $_.exception
                    Write-Error "Error occoured while invoking restapi method, Please retry" -ErrorAction Stop 
                }

            }
        }
      
   }until ($retry_count -gt $max_retry_count) 
   
    return ($JsonResponse | ConvertFrom-Json)
}

function Get-NimbleStorageAPIObject()
{
    param(
        [Parameter(Mandatory=$true)][string] $ObjectName,
        [Parameter(Mandatory=$true)][string] $APIPath,
        [Parameter(Mandatory=$true)][string] $Id,
        [System.Collections.ArrayList] $Fields
    )

    $Params = @{
        ResourcePath = $APIPath + "/$Id"
        Method = 'GET'
    }

    if ($Fields)
    {
        $Params.Add('RequestParams', @{ fields = ($Fields | Select-Object) -join ','})
    }

    $APIObject = (Invoke-NimbleStorageRestAPI @Params).data
    $DataSetType = "NimbleStorage.$ObjectName"
    $APIObject.PSTypeNames.Insert(0,$DataSetType)
    $DataSetType = $DataSetType + ".TypeName"
    $APIObject.PSObject.TypeNames.Insert(0,$DataSetType)

    return $APIObject
}

function Get-NimbleStorageAPIObjectList()
{
    param(
        [Parameter(Mandatory=$true)][string] $ObjectName,
        [Parameter(Mandatory=$true)][string] $APIPath,
        [hashtable] $Filters,
        [System.Collections.ArrayList] $Fields
    )

    # First fetch all the objects (only id and name) matching the given filter.
    # Then for each of the objects, retrieve either all the details or given fields.
    $Params = @{
        ResourcePath = $APIPath
        Method = 'GET'
        RequestParams = $Filters
    }

    # Get the list of objects matching given criteria
    $JSONResponseObject = (Invoke-NimbleStorageRestAPI @Params)
    [System.Collections.ArrayList] $APIObjects = $JSONResponseObject.data

    # We are expecting a list. If total items/objects on the array for this query are more than 1024,
    # array will send back only first 1024 objects along with total count of objects in 'totalRows'.
    if ($JSONResponseObject.endRow -and $JSONResponseObject.totalRows -and ($JSONResponseObject.endRow -lt $JSONResponseObject.totalRows))
    {
        # There are more objects. Keep getting those until we reach the end.
        while ($JSONResponseObject.endRow -lt $JSONResponseObject.totalRows)
        {
            $Params.RequestParams.startRow = $JSONResponseObject.endRow
            $JSONResponseObject = Invoke-NimbleStorageRestAPI @Params
            $APIObjects.AddRange($JSONResponseObject.data) | out-null
        }
    }

    [System.Collections.ArrayList] $APIObjectsDetailed = @()

    # Fetch needed detailes of all the objects.
    foreach ($APIObject in $APIObjects)
    {
        $Params = @{
            ObjectName = $ObjectName
            APIPath = $APIPath
            Id = $APIObject.id
        }
        if ($Fields)
        {
            $Params.Add('Fields', $Fields)
        }

        $APIObject = (Get-NimbleStorageAPIObject @Params)
        $DataSetType = "NimbleStorage.$ObjectName"
        $APIObject.PSTypeNames.Insert(0,$DataSetType)
        $DataSetType = $DataSetType + ".TypeName"
        $APIObject.PSObject.TypeNames.Insert(0,$DataSetType)
        $APIObjectsDetailed.Add($APIObject) | out-null
    }
    Write-Verbose ("Found " + $APIObjectsDetailed.Count + " objects.")
    return ,$APIObjectsDetailed
}

function New-NimbleStorageAPIObject()
{
    param(
        [Parameter(Mandatory=$true)][string] $ObjectName,
        [Parameter(Mandatory=$true)][string] $APIPath,
        [Parameter(Mandatory=$true)][hashtable] $Properties
    )

    $Params = @{
        ResourcePath = $APIPath
        Method = 'POST'
        RequestParams = $Properties
    }

    $ResponseObject = (Invoke-NimbleStorageRestAPI @Params)
    $APIObject = $ResponseObject.data
    if ($APIObject)
    {$DataSetType = "NimbleStorage.$ObjectName"
        $APIObject.PSTypeNames.Insert(0,$DataSetType)
        $DataSetType = $DataSetType + ".TypeName"
        $APIObject.PSObject.TypeNames.Insert(0,$DataSetType)
    }
    else
    {
        $APIObject = $ResponseObject
        $DataSetType = "NimbleStorage.Messages"
        $APIObject.PSTypeNames.Insert(0,$DataSetType)
        $DataSetType = $DataSetType + ".TypeName"
        $APIObject.PSObject.TypeNames.Insert(0,$DataSetType)
    }

    return $APIObject
}

function Set-NimbleStorageAPIObject()
{
    param(
        [Parameter(Mandatory=$true)][string] $ObjectName,
        [Parameter(Mandatory=$true)][string] $APIPath,
        [Parameter(Mandatory=$true)][string] $Id,
        [Parameter(Mandatory=$true)][hashtable] $Properties
    )

    $Params = @{
        ResourcePath = $APIPath + "/$Id"
        Method = 'PUT'
        RequestParams = $Properties
    }

    $ResponseObject = (Invoke-NimbleStorageRestAPI @Params)
    $APIObject = $ResponseObject.data
    if ($APIObject)
    {$DataSetType = "NimbleStorage.$ObjectName"
        $APIObject.PSTypeNames.Insert(0,$DataSetType)
        $DataSetType = $DataSetType + ".TypeName"
        $APIObject.PSObject.TypeNames.Insert(0,$DataSetType)
    }
    else
    {
        $APIObject = $ResponseObject
        $DataSetType = "NimbleStorage.Messages"
        $APIObject.PSTypeNames.Insert(0,$DataSetType)
        $DataSetType = $DataSetType + ".TypeName"
        $APIObject.PSObject.TypeNames.Insert(0,$DataSetType)
    }

    return $APIObject
}

function Remove-NimbleStorageAPIObject()
{
    param(
        [Parameter(Mandatory=$true)][string] $ObjectName,
        [Parameter(Mandatory=$true)][string] $APIPath,
        [Parameter(Mandatory=$true)][string] $Id
    )

    $Params = @{
        ResourcePath = $APIPath + "/$Id"
        Method = 'DELETE'
    }

    $APIObject = (Invoke-NimbleStorageRestAPI @Params).data
}

function Invoke-NimbleStorageAPIAction()
{
    param(
        [Parameter(Mandatory=$true)][string] $APIPath,
        [Parameter(Mandatory=$true)][string] $Action,
        [Parameter(Mandatory=$true)][hashtable] $Arguments,
        [Parameter(Mandatory=$true)][string] $ReturnType
    )

    $Params = @{
        ResourcePath = $APIPath + "/actions/$Action"
        Method = 'POST'
        RequestParams = $Arguments
    }

    if ($Arguments.id)
    {
        $id = $($Arguments.id)
        $Params.ResourcePath = $APIPath + "/$id/actions/$Action"
        $Arguments.Remove('id')
    }

    $ResponseObject = (Invoke-NimbleStorageRestAPI @Params)
    if ($ReturnType -eq "void")
    {
        # Return empty object
        return $ResponseObject
    }
    $APIObject = $ResponseObject.data
    $DataSetType = "NimbleStorage.$ReturnType"
    $APIObject.PSTypeNames.Insert(0,$DataSetType)
    $DataSetType = $DataSetType + ".TypeName"
    $APIObject.PSObject.TypeNames.Insert(0,$DataSetType)

    return $APIObject
}


function ValidateServerCertificate() 
{

    param(
    [Parameter(Mandatory,Position=0)]
        [string]$Group )


    $Code = @'
        using System;
        using System.Collections.Generic;
        using System.Net.Http;
        using System.Net.Security;
        using System.Security.Cryptography.X509Certificates;
 
        namespace CertificateCapture
        {
            public class Utility
            {
                 public static Func<HttpRequestMessage,X509Certificate2,X509Chain,SslPolicyErrors,Boolean> ValidationCallback =
                    (message, cert, chain, errors) => {
                        var newCert = new X509Certificate2(cert);
                        var newChain = new X509Chain();
                        newChain.Build(newCert);
                        CapturedCertificates.Add(new CapturedCertificate(){
                            Certificate = newCert,
                            CertificateChain = newChain,
                            PolicyErrors = errors,
                            URI = message.RequestUri
                        });
                        return true;
                    };
                public static List<CapturedCertificate> CapturedCertificates = new List<CapturedCertificate>();
            }
 
            public class CapturedCertificate
            {
                public X509Certificate2 Certificate { get; set; }
                public X509Chain CertificateChain { get; set; }
                public SslPolicyErrors PolicyErrors { get; set; }
                public Uri URI { get; set; }
            }
        }
'@


if ($PSEdition -ne 'Core'){
    $webrequest=[net.webrequest]::Create("https://$Group")
    try { $response=$webrequest.getresponse() } catch {}
    $cert=$webrequest.servicepoint.certificate
    if($cert -ne $null){
            $Thumbprint = $webrequest.ServicePoint.Certificate.GetCertHashString()
            $bytes=$cert.export([security.cryptography.x509certificates.x509contenttype]::cert)
            $tfile=[system.io.path]::getTempFileName()
            set-content -value $bytes -encoding byte -path $tfile
            $certdetails = $cert | select * | ft -AutoSize | Out-String
            if ($($GlobalImportServerCertificate))  
            {   
                try{
                    $output =import-certificate -filepath $tfile -certStoreLocation 'Cert:\localmachine\Root'
                    $certdetails = $output | select -Property Thumbprint,subject | ft -AutoSize | Out-String
                    }
                catch{
                    Write-Error "Failed to import the server certificate `n`n $_.Exception.Message"  -ErrorAction Stop
                }
                Write-Host "Successfully imported the server certificate `n $certdetails"
            }
            else{
                 if((Get-ChildItem -Path Cert:\LocalMachine\root | Where-Object {$_.Thumbprint -eq $Thumbprint})){ }                
                 else{
                        write-Error "The security certificate presented by host $Group was not issued by a trusted certificate authority. Please verify the certificate details shown below and use ImportServerCertificate command line parameter to proceed. `n $certdetails `n`n" -ErrorAction Stop 
                     }
            }

             ResolveIPtoHost $cert.subject $Group


        }
    else{
            Write-Error "Failed to import the server certificate `n`n"  -ErrorAction Stop

        }  
}else { 
        Add-Type $Code
        $Certs = [CertificateCapture.Utility]::CapturedCertificates
        $Handler = [System.Net.Http.HttpClientHandler]::new()
        $Handler.ServerCertificateCustomValidationCallback = [CertificateCapture.Utility]::ValidationCallback
        $Client = [System.Net.Http.HttpClient]::new($Handler)
        $Url = "https://$Group"
        $Result = $Client.GetAsync($Url).Result
        $cert= $Certs[-1].Certificate
        if($certs -ne $null){
            $certdetails = $cert | select -Property Thumbprint,subject | ft -AutoSize | Out-String
            if ($($GlobalImportServerCertificate))  
            {
                $bytes=$cert.export([security.cryptography.x509certificates.x509contenttype]::cert)
                $OpenFlags = [System.Security.Cryptography.X509Certificates.OpenFlags]
                $store = new-object system.security.cryptography.X509Certificates.X509Store -argumentlist "Root","LocalMachine"
                try{
                        $Store.Open($OpenFlags::ReadWrite)
                        $Store.Add($Cert)
                        $Store.Close()
                        Write-Host "Successfully imported the server certificate `n $certdetails" 
                    }
                catch{
                        Write-Error "Failed to import the server certificate `n`n $_.Exception.Message"  -ErrorAction Stop
                }
            }
            else
            {
               if((Get-ChildItem -Path Cert:\LocalMachine\root | Where-Object {$_.Thumbprint -eq $cert.Thumbprint})){ }                
                 else{
                        write-Error "The security certificate presented by host $Group was not issued by a trusted certificate authority. Please verify the certificate details shown below and use ImportServerCertificate command line parameter to proceed. `n $certdetails `n`n" -ErrorAction Stop 
                     }
            }
             ResolveIPtoHost $cert.subject $Group
        }
        else{
            Write-Error "Failed to import the server certificate `n`n"  -ErrorAction Stop
        }


}
}
function ResolveIPtoHost{

 param(
    [Parameter(Mandatory)]
        [string]$CertSubject, 
        
    [Parameter(Mandatory)]
        [string]$Group
        )

            # we will check if the host name given as input matches the host name in the certificate.
            # if IP is given as input and the certificate has hostname (FQDN) then we will use the hostname name but
            # before that we will ensurelookup from hostname to IP works.
            
            $cert_hostname = ($CertSubject.Substring(($CertSubject.IndexOf("=")+1),($CertSubject.IndexOf(",")-3))).trim()
            
            # check if the input and cert hostname matches, if yes we are good to go
            # Else the input must have ben the IP address, we will if cert hostname can be resolved to match the input hostname (IP).
            if ($Group -ne $cert_hostname)
            {
                # we will look up the DNS to resolve the cert_hostname.
                # we could do this with either cert hostname or ibput hostname(IP), but cert hostname has better chance of getting resolved.
                
                try
                {
                    $resolved_name = [System.Net.DNS]::GetHostEntry($cert_hostname).AddressList 
                    $resolved_name = $resolved_name | select -ExpandProperty IPAddressToString
                   # $Group = $resolved_name
                    # will come here if the host got resolved.
                    
                     Write-Verbose " $cert_hostname is host-name for provided input $Group IP address"
                    
                    if ($resolved_name -ne $Group)             
                    {
                        # most probably this will not happen, just to be defensive adding this code.
                        # this is the same host as the certificate hostname got resolved to the input hostname(IP)
                        # we will start using the cert hostname for all the calls from this point in this session.
                        
                        Write-Error "Unable to resolve the certificate hostname to match the provided input hostname/IP. `n`n $_.Exception.Message"  -ErrorAction Stop
                    }
                    else
                    {
                        # we are good to go.
                        Write-Verbose " Resolved name and input name matches: $Group IP address"
                        $global:Group = $cert_hostname
                    }

                }
                catch 
                {
                    # unable to resolve the cert hostname. Host not reachable. Error out!.
                    Write-Error "Unable to resolve the certificate hostname. Host not reachable. `n`n $_.Exception.Message"  -ErrorAction Stop
                }

            }
            else
            {
                # we are good to go.
                
                Write-Verbose "Host-name given as input matches with certificate name"
            }
             


}

Function APIExceptionHandler
{
   #Exception message handle differently for core and non core environment
   #GetResponseStream method does not work in core environment

   if(Get-Member -inputobject $_.Exception.Response -name "GetResponseStream" -Membertype Method)
   {
       $JsonResponse = $_.Exception.Response.GetResponseStream()
       $reader = New-Object System.IO.StreamReader($JsonResponse)
       $reader.BaseStream.Position = 0
       $reader.DiscardBufferedData()
       $responseBody = $reader.ReadToEnd();
       if (($responseBody | ConvertFrom-Json).messages -ne $null)
       {
           foreach( $errorMsg in ($responseBody | ConvertFrom-Json).messages) {
           if($errorMsg.text -ne $null) {$exceptionString += $errorMsg.text + " "}
       }
       throw [System.Exception] $exceptionString
       }
       else
       {
           throw $_.Exception
       }
   }
   else
   {
        
        $responseBody = $_.ErrorDetails
       if (($responseBody | ConvertFrom-Json).messages -ne $null)
       {
           foreach( $errorMsg in ($responseBody | ConvertFrom-Json).messages) {
           if($errorMsg.text -ne $null) {$exceptionString += $errorMsg.text + " "}
       }
       throw [System.Exception] $exceptionString
       }
       else
       {
           throw $_.Exception
       }
   }       
}
# SIG # Begin signature block
# MIIiUwYJKoZIhvcNAQcCoIIiRDCCIkACAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDidTDS8U60Ltza
# 425ucKonwVu33bD4r6ZjtGmnBlXjaqCCEIwwggVRMIIEOaADAgECAhAqlTIY4QtL
# e2/RSyqeww0mMA0GCSqGSIb3DQEBCwUAMIG9MQswCQYDVQQGEwJVUzEXMBUGA1UE
# ChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
# cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv
# cml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290
# IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE2MDUxMjAwMDAwMFoXDTI2MDUx
# MTIzNTk1OVowgZExCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jw
# b3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazFCMEAGA1UE
# AxM5U3ltYW50ZWMgQ2xhc3MgMyBFeHRlbmRlZCBWYWxpZGF0aW9uIENvZGUgU2ln
# bmluZyBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApZCU
# ss4Vva9QGeDEzgpo6PhoHDzlK84LS1feob0ads/HnzSxAG14+SblzJmsdMae4Gzh
# LZEgk4drboRs1S5SF9CP2ers/Owg8g3aP22cJdHb+yDs8ND/SxC2uTkRjfYMokxP
# uB33fdDlz/dcM4BmpNYib3vjeBcGBfTGxpS3/stWKn4P+hjzSANNp24WtxUbfdwv
# 20MOKR7ReG9oGBu3gst+WI/Y0ph/kE27xws8cf7MxMv2o0IQrB3Kg/yRviyqgK+3
# mWlGcdOVIJnNUS6V+KwSHnzitCJpLgvAF3yg/e1cpi4iK2X/fc0xpGOs3yLlk3yi
# ToLCEyy/HIsiPatm7QIDAQABo4IBdTCCAXEwLgYIKwYBBQUHAQEEIjAgMB4GCCsG
# AQUFBzABhhJodHRwOi8vcy5zeW1jZC5jb20wEgYDVR0TAQH/BAgwBgEB/wIBADBg
# BgNVHSAEWTBXMFUGBWeBDAEDMEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8vZC5zeW1j
# Yi5jb20vY3BzMCUGCCsGAQUFBwICMBkaF2h0dHBzOi8vZC5zeW1jYi5jb20vcnBh
# MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9zLnN5bWNiLmNvbS91bml2ZXJzYWwt
# cm9vdC5jcmwwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwMwDgYDVR0PAQH/BAQDAgEG
# MCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0yLTM4ODAdBgNV
# HQ4EFgQUq4sRSQsqAmJ1SpvFAiCghNJL+N4wHwYDVR0jBBgwFoAUtnf6aUhHn1MS
# 1cLqBzJ2B9GXBxkwDQYJKoZIhvcNAQELBQADggEBADAxqBF+Ga4dNNPS18ywHMoB
# oUoWX2jFyk0FWQH0/IUKp88fsBwgZAlBGBK0490yM3KZEytIXUmWESLnucRvBTBN
# bKwzvk1PImLW8WcqFnvGjI4OuZYAnQ5kAZJk6jf4BH4RYm+MLJxdC7j/3X9AzOiI
# 8p0mNEm0H735Wz94cy6B+mka85SPlTB+wrTYlSXSev6KOOiwb/ZvOfDO+oRWEH9W
# p8UUYLRPWjmBSqzhMmBLDhSg9R6hSEOXWZG9HWhmmDAAi8JZ1H72hRt6f7rbZWgO
# rW5ztlQ59GgDhXyhvFOzO6GCP3gkNQKkroWGwgXngHVcBZF57aqNh2kmiheB58Mw
# ggWNMIIDdaADAgECAgphLSPLAAAAAAAhMA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAMTIE1pY3Jvc29m
# dCBDb2RlIFZlcmlmaWNhdGlvbiBSb290MB4XDTExMDIyMjE5NDYzOVoXDTIxMDIy
# MjE5NTYzOVowgb0xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5j
# LjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMp
# IDIwMDggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE4
# MDYGA1UEAxMvVmVyaVNpZ24gVW5pdmVyc2FsIFJvb3QgQ2VydGlmaWNhdGlvbiBB
# dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHYTdesQE0
# 22LXFZv/WFqMIyPWYI6R15CYg3rmWBk4jMX25WSFtKJx++29udrNTQC0yC1zpcdp
# cZUfOTyyRAec6A76TUrEId8pYY8yImGCxYcfbox8XxYgUUTRcE9X6uMc48x57ljY
# DsKzRZPALOeaFyt7ADd6QTN44TPi8xAaf4csvvb190Li5b+HYolfAEvfxd3kdUQy
# QToecW5pywt1RgjRytIrldDP+7lAa2SMV038ExF5hO1eVPY0nwgB8xAlBhdK2vEd
# emZrmGBmpNnv0i6C8fDvCepEyRVq4gNuM9Osn1UAx/YIapS5X9zgM/GEYPlbJxG0
# /Bbyu1ZqgCWNAgMBAAGjgcswgcgwEQYDVR0gBAowCDAGBgRVHSAAMA8GA1UdEwEB
# /wQFMAMBAf8wCwYDVR0PBAQDAgGGMB0GA1UdDgQWBBS2d/ppSEefUxLVwuoHMnYH
# 0ZcHGTAfBgNVHSMEGDAWgBRi+wohW39DbhHaCVRQa/XSlnHxnjBVBgNVHR8ETjBM
# MEqgSKBGhkRodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0
# cy9NaWNyb3NvZnRDb2RlVmVyaWZSb290LmNybDANBgkqhkiG9w0BAQUFAAOCAgEA
# WUdSmjRDPbATxDK4+DfZVVANKqRnZzOUyf/Y9QysjVo5g0htsrtDj3sL81uvNHpP
# nrcI5/4BOBHeLzucPpYLslCC5rVVtGCWE0jtJy0lAnI4s/NlWYpiM3bVbPmV9J01
# TIOgwWf8g7V2k8boUhtZubqFlAo70SgFGxkM8yMXZUZ4DtlzSsC8PcBI4aT4h+UW
# 4VaHZTjhABeiHkQr9k9aKTywG7hfZUtI62qYKV5X4vVi2ENHsy5qE0GnfTgjBg33
# XOZ/us1lBJJSUiZgqKM8Ig2tt7pMMm9S3qubccme6L6fXqatd9dtJZVWKhhDwszB
# buHO30Xv/rdRKnyYtP5mg59rbOx01Z8yHyQ7QzrRBB0ASlul0m1mTMjBTezDATXB
# uHn94Mb2qCmgko09Q3d4ph5MwjgEWOs6gzQExjROE0WyW3IvcmpP5Rp8GGtE48hY
# H45xDIBrTit7PPhK45OSxLCavKZht9N8ynD1+v/NB6LZ6BHMbERQn3GwgtI2pllz
# vbcLW2mGe6Ufx53B0grdLkzCMbaHKo0Qevqnfjxf8LCg9UUF3nkSegKF8R6K05mH
# FylcvnVY1nkBBpVjOfsFFsc9SVGs+muYLcyXbqyzw2+CnACWfyQN6VlAYbNk27n5
# QaG57tlYjf8EV87Y/ur/Z1vOE/yc/PbhZs2joN13EbIwggWiMIIEiqADAgECAhAo
# MknpQET0/8yu7uTbXTF2MA0GCSqGSIb3DQEBCwUAMIGRMQswCQYDVQQGEwJVUzEd
# MBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVj
# IFRydXN0IE5ldHdvcmsxQjBABgNVBAMTOVN5bWFudGVjIENsYXNzIDMgRXh0ZW5k
# ZWQgVmFsaWRhdGlvbiBDb2RlIFNpZ25pbmcgQ0EgLSBHMzAeFw0xODExMTkwMDAw
# MDBaFw0yMTExMTgyMzU5NTlaMIIBIzETMBEGCysGAQQBgjc8AgEDEwJVUzEZMBcG
# CysGAQQBgjc8AgECDAhEZWxhd2FyZTEZMBcGCysGAQQBgjc8AgEBDAhTYW4gSm9z
# ZTEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzU2OTky
# NjUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMREwDwYDVQQHDAhT
# YW4gSm9zZTErMCkGA1UECgwiSGV3bGV0dCBQYWNrYXJkIEVudGVycHJpc2UgQ29t
# cGFueTEWMBQGA1UECwwNTmltYmxlc3RvcmFnZTErMCkGA1UEAwwiSGV3bGV0dCBQ
# YWNrYXJkIEVudGVycHJpc2UgQ29tcGFueTCCASIwDQYJKoZIhvcNAQEBBQADggEP
# ADCCAQoCggEBAKWSqwdskNaMZ0X9Nqn4RKr2Bc131ZU5pS3FWyBGPRumS2sFtCTa
# frdZ9i1xQL0dF3KcNHFFBAnkM/Ig4+UNOR/TIlAD7ssnDxNxfYG07GXxPmAlogoI
# 2s6CWQ1mmLjZoJPHcGqcBsjKvn9sw/OZ1buOjB9SPBn02sh3S2L7dFnlQ1LuJDYa
# sL0BlOzye9leSysvqyhDwMsYv3K7wNpyLoxi7Ee4z/ZBupMZY3LY8F7RMg6W2aHK
# zzqDaP/X867mLqHedB6aK80aQY3C4MVFfOqiLHQFIG7/gVa16aO4U1q8HDUZswRx
# IeiRMOaHje8VRdhtWUU2U+CQC4/RRx1GLsMCAwEAAaOCAV8wggFbMAkGA1UdEwQC
# MAAwDgYDVR0PAQH/BAQDAgeAMCsGA1UdHwQkMCIwIKAeoByGGmh0dHA6Ly9yaC5z
# eW1jYi5jb20vcmguY3JsMGAGA1UdIARZMFcwVQYFZ4EMAQMwTDAjBggrBgEFBQcC
# ARYXaHR0cHM6Ly9kLnN5bWNiLmNvbS9jcHMwJQYIKwYBBQUHAgIwGQwXaHR0cHM6
# Ly9kLnN5bWNiLmNvbS9ycGEwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwMwHwYDVR0j
# BBgwFoAUq4sRSQsqAmJ1SpvFAiCghNJL+N4wHQYDVR0OBBYEFCwOW0/Kegfs4BMM
# 4OMor35vnOjTMFcGCCsGAQUFBwEBBEswSTAfBggrBgEFBQcwAYYTaHR0cDovL3Jo
# LnN5bWNkLmNvbTAmBggrBgEFBQcwAoYaaHR0cDovL3JoLnN5bWNiLmNvbS9yaC5j
# cnQwDQYJKoZIhvcNAQELBQADggEBAHFMScjMHXHXJv6cZj0Kwh1Uphw2DfoLL5rz
# dOYd0vT8PH3WU3WwfJssqsU6LxvPIQlVj75z73PoegdDYLOH2NRfxMGYuw9Ru+4P
# vBEjWpJOeV3j5rM8yubcgF6twn6hkDbI5GsfaK7MJgovXOglzKuV7mCkJMHb8LDT
# qPE6mT9zDUR7YRaavhxV9YiiCnU7RLZd7HwtmJvygF/xkyJg+XYoTolQw7ZEtBVb
# UScbXdDmJO0BWpWlkAF+gMmIBlcdoZ7Avwf4DdQdp+KiDKRlMFTgp0fJ7uzfTZDY
# P+yEYst/1wNjCavw8EoALakact7/SR/x1WP0hR5qALp/jg4gdqIxghEdMIIRGQIB
# ATCBpjCBkTELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0
# aW9uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUIwQAYDVQQDEzlT
# eW1hbnRlYyBDbGFzcyAzIEV4dGVuZGVkIFZhbGlkYXRpb24gQ29kZSBTaWduaW5n
# IENBIC0gRzMCECgySelARPT/zK7u5NtdMXYwDQYJYIZIAWUDBAIBBQCgfDAQBgor
# BgEEAYI3AgEMMQIwADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEE
# AYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg7TzXgKubcKaV
# eQhqpQOj/d6q+StVfJonqS/zVwA2RbYwDQYJKoZIhvcNAQEBBQAEggEANL/puypC
# 6uvp+NjkjhBVxpaW7DPDHsxPEWpuGjhvdMJrZidNnvNBmDKnoHGG/Lfu3LHQ184D
# Xbu2EcA1Kf5IssjIYKcpy/h0xdf4IHowK0x7rj2pvpknLjopdmcnGz7PCRDkyKfd
# SoyFhtUFRmJW0azJejH+sT76DBhbxoQGa3mX8VGh/Fmln8nNxSt1s/Mt9dFRAUze
# yr9sauUxWqSZRPgGYc9GtUEYeg6hybOyjkQKHxNQF6lilTlkWn7rlIXVrJ2kT3KQ
# iGLV0gBjdJHvVX9PyOGwW0ldDVzK0pGZhn91OQ8nwuET2SZaX3GV4Zo/hVvNzCTx
# Ntn7jvq7DetQEKGCDskwgg7FBgorBgEEAYI3AwMBMYIOtTCCDrEGCSqGSIb3DQEH
# AqCCDqIwgg6eAgEDMQ8wDQYJYIZIAWUDBAIBBQAweAYLKoZIhvcNAQkQAQSgaQRn
# MGUCAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFlAwQCAQUABCAH0bZOnUurzEIsQou0
# ULNnqSZk/Ww9jIVZhd6atv/jQQIRAKSxzG8IEazzW61KfdPLxAIYDzIwMjAwNTE1
# MDM0MzQyWqCCC7swggaCMIIFaqADAgECAhAEzT+FaK52xhuw/nFgzKdtMA0GCSqG
# SIb3DQEBCwUAMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNI
# QTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwHhcNMTkxMDAxMDAwMDAwWhcN
# MzAxMDE3MDAwMDAwWjBMMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs
# IEluYy4xJDAiBgNVBAMTG1RJTUVTVEFNUC1TSEEyNTYtMjAxOS0xMC0xNTCCASIw
# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOlkNZz6qZhlZBvkF9y4KTbMZwlY
# hU0w4Mn/5Ts8EShQrwcx4l0JGML2iYxpCAQj4HctnRXluOihao7/1K7Sehbv+EG1
# HTl1wc8vp6xFfpRtrAMBmTxiPn56/UWXMbT6t9lCPqdVm99aT1gCqDJpIhO+i4It
# xpira5u0yfJlEQx0DbLwCJZ0xOiySKKhFKX4+uGJcEQ7je/7pPTDub0ULOsMKCcl
# gKsQSxYSYAtpIoxOzcbVsmVZIeB8LBKNcA6Pisrg09ezOXdQ0EIsLnrOnGd6OHdU
# QP9PlQQg1OvIzocUCP4dgN3Q5yt46r8fcMbuQhZTNkWbUxlJYp16ApuVFKMCAwEA
# AaOCAzgwggM0MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB
# /wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0gBIIBtjCCAbIwggGhBglghkgBhv1sBwEw
# ggGSMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMIIB
# ZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkA
# cwAgAEMAZQByAHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUA
# cwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkA
# QwBlAHIAdAAgAEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkA
# aQBuAGcAIABQAGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMA
# aAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIA
# ZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkA
# IAByAGUAZgBlAHIAZQBuAGMAZQAuMAsGCWCGSAGG/WwDFTAfBgNVHSMEGDAWgBT0
# tuEgHf4prtLkYaWyoiWyyBc1bjAdBgNVHQ4EFgQUVlMPwcYHp03X2G5XcoBQTOTs
# nsEwcQYDVR0fBGowaDAyoDCgLoYsaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3No
# YTItYXNzdXJlZC10cy5jcmwwMqAwoC6GLGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNv
# bS9zaGEyLWFzc3VyZWQtdHMuY3JsMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUH
# MAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDov
# L2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRFRpbWVz
# dGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsFAAOCAQEALoOhRAVKBOO5MlL62YHw
# Grv4CY0juT3YkqHmRhxKL256PGNuNxejGr9YI7JDnJSDTjkJsCzox+HizO3LeWvO
# 3iMBR+2VVIHggHsSsa8Chqk6c2r++J/BjdEhjOQpgsOKC2AAAp0fR8SftApoU39a
# EKb4Iub4U5IxX9iCgy1tE0Kug8EQTqQk9Eec3g8icndcf0/pOZgrV5JE1+9uk9lD
# xwQzY1E3Vp5HBBHDo1hUIdjijlbXST9X/AqfI1579JSN3Z0au996KqbSRaZVDI/2
# TIryls+JRtwxspGQo18zMGBV9fxrMKyh7eRHTjOeZ2ootU3C7VuXgvjLqQhsUwm0
# 9zCCBTEwggQZoAMCAQICEAqhJdbWMht+QeQF2jaXwhUwDQYJKoZIhvcNAQELBQAw
# ZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ
# d3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBS
# b290IENBMB4XDTE2MDEwNzEyMDAwMFoXDTMxMDEwNzEyMDAwMFowcjELMAkGA1UE
# BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj
# ZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVz
# dGFtcGluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3QMu5L
# zY9/3am6gpnFOVQoV7YjSsQOB0UzURB90Pl9TWh+57ag9I2ziOSXv2MhkJi/E7xX
# 08PhfgjWahQAOPcuHjvuzKb2Mln+X2U/4Jvr40ZHBhpVfgsnfsCi9aDg3iI/Dv9+
# lfvzo7oiPhisEeTwmQNtO4V8CdPuXciaC1TjqAlxa+DPIhAPdc9xck4Krd9AOly3
# UeGheRTGTSQjMF287DxgaqwvB8z98OpH2YhQXv1mblZhJymJhFHmgudGUP2UKiyn
# 5HU+upgPhH+fMRTWrdXyZMt7HgXQhBlyF/EXBu89zdZN7wZC/aJTKk+FHcQdPK/P
# 2qwQ9d2srOlW/5MCAwEAAaOCAc4wggHKMB0GA1UdDgQWBBT0tuEgHf4prtLkYaWy
# oiWyyBc1bjAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzASBgNVHRMB
# Af8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcD
# CDB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj
# ZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t
# L0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHoweDA6oDigNoY0
# aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNz
# dXJlZElEUm9vdENBLmNybDBQBgNVHSAESTBHMDgGCmCGSAGG/WwAAgQwKjAoBggr
# BgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzALBglghkgBhv1s
# BwEwDQYJKoZIhvcNAQELBQADggEBAHGVEulRh1Zpze/d2nyqY3qzeM8GN0CE70uE
# v8rPAwL9xafDDiBCLK938ysfDCFaKrcFNB1qrpn4J6JmvwmqYN92pDqTD/iy0dh8
# GWLoXoIlHsS6HHssIeLWWywUNUMEaLLbdQLgcseY1jxk5R9IEBhfiThhTWJGJIdj
# jJFSLK8pieV4H9YLFKWA1xJHcLN11ZOFk362kmf7U2GJqPVrlsD0WGkNfMgBsbko
# dbeZY4UijGHKeZR+WfyMD+NvtQEmtmyl7odRIeRYYJu6DC0rbaLEfrvEJStHAgh8
# Sa4TtuF8QkIoxhhWz0E0tmZdtnR79VYzIi8iNrJLokqV2PWmjlIxggJNMIICSQIB
# ATCBhjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz
# c3VyZWQgSUQgVGltZXN0YW1waW5nIENBAhAEzT+FaK52xhuw/nFgzKdtMA0GCWCG
# SAFlAwQCAQUAoIGYMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG
# 9w0BCQUxDxcNMjAwNTE1MDM0MzQyWjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBQD
# Jb1QXtqWMC3CL0+gHkwovig0xTAvBgkqhkiG9w0BCQQxIgQg7bCYtf6FEWOjeOkr
# qNetgy7yEAhgjk0BLsq/BNb1zAgwDQYJKoZIhvcNAQEBBQAEggEAsAguSl0CSTrU
# mBQqFmvxloS7xmVA6Jf5Mup6S0eGhWHJlWgRMw72tfgE6DENwa6s3W4TU0/r9eIL
# 1yAXqrUB+SJGg0IlWARqM+H5olFfFQtMiVwa9bXVZO0O/yN7SuJR+Hq1yfjGc6x2
# /jlY1uF6guy+I7hT4TqvHJG0V0VNtE8Oc/eH6xaa8ItDycB95bOyZGWeEXbQNRW1
# 0ly/lmCslA0tWq3Xf0WmjZGRqUOtq1xy/QDi1qVKpFs8Pxn9xrW1WDoyUBSRfEhh
# j9nhy/E++xRIOVYvVBJfUowq2GrOMPk7Ms6+I6PQfwQrYIIhH+lUG7tmpUwFfUnq
# JM74buPEsQ==
# SIG # End signature block