UMN-Infoblox.psm1

###
# Copyright 2017 University of Minnesota, Office of Information Technology

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with Foobar. If not, see <http://www.gnu.org/licenses/>.
## Modules for infoblox
# It is assumed that a Connection has already been established and the cookie is being passed to many of these functions.
# Use 'Connect-Infoblox' to return a $cookie --

#region Connect-Infoblox
Function Connect-Infoblox{
<#
    .SYNOPSIS
        Core function - retrieve cookie for API access to infoblox
    .DESCRIPTION
    
    .PARAMETER InfobloxCred
        PS credential of user that has access

    .PARAMETER uriBase
        FQDN of infoblox host including version, example 'https://myhost.mycompany.com/wapi/v1.7.1'

    .PARAMETER SkipCertificateCheck
        Ignore bad SSL Certificates

    .EXAMPLE
        Connect-Infoblox -InfobloxCreds $infobloxCreds -uriBase $uriBase
    .NOTES
        For legacy automation systems dealing with cookies -
        -UseBasicParsing is included on the InvokeWebRequest - needed parsing for Orchestrator
#>



    [CmdletBinding()]
    param(

        [ValidateNotNullOrEmpty()]
        [System.Management.Automation.PSCredential]$InfobloxCreds,

        [parameter(Mandatory)]
        [string]$uriBase,

        [switch]$SkipCertificateCheck
    )
     
     if ($SkipCertificateCheck -and $PSVersionTable.PSVersion.Major -lt 6){
add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@

            [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
    }
            $uri  = "$uriBase/grid"
            if ($SkipCertificateCheck -and $PSVersionTable.PSVersion.Major -eq 6){$grid = Invoke-WebRequest -uri $uri -Method Get -Credential $InfobloxCreds -SessionVariable cookie -UseBasicParsing -SkipCertificateCheck}
            else{$grid = Invoke-WebRequest -uri $uri -Method Get -Credential $InfobloxCreds -SessionVariable cookie -UseBasicParsing}
            return ($cookie)
}
#endregion

#region Get-InfobloxAlias
Function Get-InfobloxAlias{

<#
.SYNOPSIS
    Get Alias by FQDN
.DESCRIPTION
    
.EXAMPLE
    Get-InfobloxAlias -cookie $cookie -uriBase $uriBase -alias $alias
.NOTES
    General notes
#>

    
    [CmdletBinding()]
    Param
    (
        [ValidateNotNullOrEmpty()]
        [Microsoft.PowerShell.Commands.WebRequestSession]$cookie,

        [ValidateNotNullOrEmpty()]
        [string]$uriBase,

        [ValidateNotNullOrEmpty()]
        [string]$alias
    )
    
    $uri  = "$uriBase/record:cname?name:=$alias"
    Invoke-RestMethod -uri $uri -Method Get -WebSession $cookie
}
#endregion

#region Get-InfobloxHost
Function Get-InfobloxHost{
<#
    .SYNOPSIS
        Get host by FQDN
    .DESCRIPTION
        Get host by FQDN
    .EXAMPLE
        Get-InfobloxHost -cookie $cookie -uriBase $uriBase -host_name $host_name
    .NOTES
        General notes
#>
    
    [CmdletBinding()]
    Param
    (
        [ValidateNotNullOrEmpty()]
        [Microsoft.PowerShell.Commands.WebRequestSession]$cookie,

        [ValidateNotNullOrEmpty()]
        [string]$uriBase,

        [ValidateNotNullOrEmpty()]
        [string]$host_name
    )
    
    $host_name = $host_name.ToLower()
    $uri  = "$uriBase/record:host?name=$host_name&_return_fields=aliases,ipv4addrs"
    Invoke-RestMethod -uri $uri -Method Get -WebSession $cookie
}
#endregion

#region Get-InfobloxIPv4Net
Function Get-InfobloxIPv4Net{
<#
.SYNOPSIS
    Get IPv4 Network
.DESCRIPTION
    Get a network stack from infoblox
.EXAMPLE
    Get-InflobloxIPv4Net -cookie $cookie -uriBase $uriBase -ipv4Net $ipv4net
.NOTES
    General notes
#>


    [CmdletBinding()]
    Param
    (
        [ValidateNotNullOrEmpty()]
        [Microsoft.PowerShell.Commands.WebRequestSession]$cookie,

        [ValidateNotNullOrEmpty()]
        [string]$uriBase,

        [ValidateNotNullOrEmpty()]
        [string]$ipv4Net
    )
    $uri = "$uriBase/network?network=$ipv4Net"
    Invoke-RestMethod -uri $uri -Method Get -WebSession $cookie
}
#endregion

#region Get-InfobloxIPv4IP
Function Get-InfobloxIPv4IP{
<#
.SYNOPSIS
    Get reference to specific IPv4 address
.DESCRIPTION
    
.EXAMPLE
    Get-InflobloxIPv4IP -cookie $cookie -uriBase $uriBase -ipv4Address $ipv4Address
.NOTES
    Check 'types' and look for HOST (NOT BULKHOST). If types contains HOST, its in use
#>



    [CmdletBinding()]
    Param
    (
        [ValidateNotNullOrEmpty()]
        [Microsoft.PowerShell.Commands.WebRequestSession]$cookie,

        [ValidateNotNullOrEmpty()]
        [string]$uriBase,

        [ValidateNotNullOrEmpty()]
        [string]$ipv4Address
    )

    $uri = "$uriBase/ipv4address?ip_address=$ipv4Address"
    Invoke-RestMethod -uri $uri -Method Get -WebSession $cookie

}
#endregion

#region Get-InfobloxIPv4IPs
Function Get-InfobloxIPv4IPs{
<#
.SYNOPSIS
    Get reference to specific IPv4 network
.DESCRIPTION
    Get all Hosts in a specific IPv4 Network
.EXAMPLE
    Get-InfobloxIPv4ips -cookie $cookie -uriBase $uriBase -ipv4net $ipv4net
.NOTES
    
#>


    [CmdletBinding()]
    Param
    (
        [ValidateNotNullOrEmpty()]
        [Microsoft.PowerShell.Commands.WebRequestSession]$cookie,

        [ValidateNotNullOrEmpty()]
        [string]$uriBase,

        [ValidateNotNullOrEmpty()]
        [string]$ipv4Net
    )

    $uri = "$uriBase/ipv4address?network=$ipv4Net"
    Invoke-RestMethod -uri $uri -Method Get -WebSession $cookie

}
#endregion

#region Get-InfobloxIPv4Available
Function Get-InfobloxIPv4Available{

<#
.SYNOPSIS
    Get an available IPv4 address
.DESCRIPTION
    Check for, and retrieve an available IPv4 address
.EXAMPLE
    Get-InfobloxIP4Available -cookie $cookie -uriBase $uriBase -ipv4net $ipv4net -domainSuffix -$domainSuffix
.NOTES
    
#>



    [CmdletBinding()]
    Param
    (
        [ValidateNotNullOrEmpty()]
        [Microsoft.PowerShell.Commands.WebRequestSession]$cookie,

        [ValidateNotNullOrEmpty()]
        [string]$uriBase,

        [ValidateNotNullOrEmpty()]
        [string]$ipv4Net,
        
        [ValidateNotNullOrEmpty()]
        [string]$domainSuffix
    )
    
    $ipv4IPs = Get-InfobloxIPv4IPs -cookie $cookie -uriBase $uriBase -ipv4Net $ipv4Net
    $ipFound = $false
    foreach ($ip in $ipv4IPs){
        if ($ip.types -notcontains "HOST" -and $ip.lease_state -eq "FREE") # IF it returns a single entry this should be the bulk host. < 1 = router, >1 its tied to host record so don't touch it
        {
            $ipFound = $true 
            return $ip.ip_address
        }
    }
    if(-not $ipFound){return "unable to find IPv4 Address, exiting"}

}
#endregion

#region Get-InfobloxIPv6IP
Function Get-InfobloxIPv6IP{
<#
.SYNOPSIS
    Get reference to specific IPv6 address
.DESCRIPTION
    
.EXAMPLE
    Get-InflobloxIPv6IP -cookie $cookie -uriBase $uriBase -ipv6Address $ipv6Address
.NOTES
    
#>



    [CmdletBinding()]
    Param
    (
        [ValidateNotNullOrEmpty()]
        [Microsoft.PowerShell.Commands.WebRequestSession]$cookie,

        [ValidateNotNullOrEmpty()]
        [string]$uriBase,

        [ValidateNotNullOrEmpty()]
        [string]$ipv6address
    )

    $uri  = "$uriBase/record:host_ipv6addr?ipv6addr=$ipv6address"
    Invoke-RestMethod -uri $uri -Method Get -WebSession $cookie

}
#endregion

#region Get-InfobloxIPbyMac
function Get-InfobloxIPbyMac {
    <#
    .SYNOPSIS
        Get IP information by MAC address
    .DESCRIPTION
        
    .EXAMPLE
        Get-InfobloxIPbyMac -cookie $cookie -uriBase $uriBase -ipv4net $ipv4net -mac $mac
    .NOTES
    #>

    
    [CmdletBinding()]
    param(

        [ValidateNotNullOrEmpty()]
        [Microsoft.PowerShell.Commands.WebRequestSession]$cookie,

        [string]$uriBase,

        [ValidateNotNullOrEmpty()]
        [string]$ipv4Net,

        [ValidatePattern("([a-zA-Z0-9]{2}:){5}[a-zA-Z0-9]{2}")]
        [string]$mac

    )

    $uri = "$uriBase/ipv4address?network=$ipv4Net&mac_address=$mac"
    $ipv4IPs =  Invoke-RestMethod -uri $uri -Method Get -WebSession $cookie
    $activeIP = $ipv4IPs | Where-Object {$_.lease_state -eq 'ACTIVE'}
    if ($activeIP -ne $null){return ($activeIP.ip_address)}
    else{throw "Failed to get single IP info $ipv4IPs"}
}
#endregion

#region New-InfobloxHost
Function New-InfobloxHost{

<#
.SYNOPSIS
    Create a new Host object with IPv4 and IPv6 Address
.DESCRIPTION
    
.EXAMPLE
    New-InfobloxHost -cookie $cookie -uribase $uriBase -host_name $host_name -new_IPv4 $new_ipV4 -new_IPv6 $new_ipv6 -ipv4net $ipv4net
.NOTES
    Use other module commands to generate a list of IP's to associate to a new host entry.
#>




    [CmdletBinding()]
    Param
    (
        [ValidateNotNullOrEmpty()]
        [Microsoft.PowerShell.Commands.WebRequestSession]$cookie,

        [ValidateNotNullOrEmpty()]
        [string]$uriBase,

        [ValidateNotNullOrEmpty()]
        [string]$host_name,

        [ValidateNotNullOrEmpty()]
        [string]$new_IPv4,

        [string]$new_IPv6,

        [ValidateNotNullOrEmpty()]
        [string]$ipv4Net
    )
    $host_name = $host_name.ToLower()
    $uri = "$uriBase/record:host"
    if ($new_IPv6)
    {
        $JSON = @{name=$host_name;ipv4addrs=@(@{ipv4addr=$new_IPv4;configure_for_dhcp=$true;match_client="RESERVED"});
            ipv6addrs=@(@{ipv6addr=$new_IPv6});
            extattrs=@{Custom1=@{value=$ipv4Net};Custom2=@{value="default 2 hour"}}} | ConvertTo-Json
    }
    else
    {
        $JSON = @{name=$host_name;ipv4addrs=@(@{ipv4addr=$new_IPv4;configure_for_dhcp=$true;match_client="RESERVED"});
            extattrs=@{Custom1=@{value=$ipv4Net};Custom2=@{value="default 2 hour"}}} | ConvertTo-Json
    }
    Invoke-RestMethod -Uri $uri -Body $JSON -ContentType "application/json" -Method Post -WebSession $cookie

}
#endregion

#region New-InfobloxDhcpReservation
Function New-InfobloxDhcpReservation{
<#
.SYNOPSIS
    Create a new host object for doing DHCP reservations
.DESCRIPTION
    For use with DHCP reservations by mac address
.EXAMPLE
    New-InfobloxDhcpReservation -cookie $cookie -uriBase $uriBase -host_name $host_name -new_IPv4 $new_ipV4 -ipv4Net $ipv4net -mac $mac
.NOTES
    
#>

    [CmdletBinding()]
    Param
    (
        [ValidateNotNullOrEmpty()]
        [Microsoft.PowerShell.Commands.WebRequestSession]$cookie,

        [ValidateNotNullOrEmpty()]
        [string]$uriBase,

        [ValidateNotNullOrEmpty()]
        [string]$host_name,

        [ValidatePattern("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")]
        [string]$new_IPv4,

        [ValidateNotNullOrEmpty()]
        [string]$ipv4Net,

        [ValidatePattern("([a-zA-Z0-9]{2}:){5}[a-zA-Z0-9]{2}")]
        [string]$mac
    )
    $host_name = $host_name.ToLower()
    $uri = "$uriBase/record:host"
    $JSON = @{name=$host_name;ipv4addrs=@(@{ipv4addr=$new_IPv4;configure_for_dhcp=$true;match_client="MAC_ADDRESS";mac=$mac});
        extattrs=@{Custom1=@{value=$ipv4Net};Custom2=@{value="default 2 hour"}}} | ConvertTo-Json    
    Invoke-RestMethod -Uri $uri -Body $JSON -ContentType "application/json" -Method Post -WebSession $cookie

}
#endregion

#region Remove-InfobloxHost
Function Remove-InfobloxHost{
<#
.SYNOPSIS
    Remove Host Record
.DESCRIPTION
    
.EXAMPLE
    Remove-InfobloxHost -cookie $cookie -uriBase $uriBase -host_name $host_name
.NOTES
    General notes
#>


  ##

    [CmdletBinding()]
    Param
    (
        [ValidateNotNullOrEmpty()]
        [Microsoft.PowerShell.Commands.WebRequestSession]$cookie,

        [ValidateNotNullOrEmpty()]
        [string]$uriBase,

        [ValidateNotNullOrEmpty()]
        [string]$host_name
    )
    $host_name = $host_name.ToLower()
    ## Validate Host exists
    $ibHost = Get-InfobloxHost -cookie $cookie -uriBase $uriBase -host_name $host_name
    if ($ibHost -eq $null){throw "Infoblox Host Doesn't Exist"}
    # Aliases must be removed before removing the host record
    if ($ibHost.aliases){}## waiting for enough permissions to see all the aliases and remove them
    $host_ref = $ibHost._ref
       
    $uri  = "$uriBase/$host_ref"
    #validate response
    if ((Invoke-RestMethod -uri $uri -Method Delete -WebSession $cookie) -ne $host_ref){throw "Infoblox Delete Failed"}

}
#endregion

Export-ModuleMember -Function *
# SIG # Begin signature block
# MIIaxgYJKoZIhvcNAQcCoIIatzCCGrMCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU5LNMEgy74o26LmxizC7cPtOl
# jlegghW3MIIEmTCCA4GgAwIBAgIPFojwOSVeY45pFDkH5jMLMA0GCSqGSIb3DQEB
# BQUAMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQg
# TGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNV
# BAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEdMBsGA1UEAxMUVVROLVVTRVJG
# aXJzdC1PYmplY3QwHhcNMTUxMjMxMDAwMDAwWhcNMTkwNzA5MTg0MDM2WjCBhDEL
# MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
# BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKjAoBgNVBAMT
# IUNPTU9ETyBTSEEtMSBUaW1lIFN0YW1waW5nIFNpZ25lcjCCASIwDQYJKoZIhvcN
# AQEBBQADggEPADCCAQoCggEBAOnpPd/XNwjJHjiyUlNCbSLxscQGBGue/YJ0UEN9
# xqC7H075AnEmse9D2IOMSPznD5d6muuc3qajDjscRBh1jnilF2n+SRik4rtcTv6O
# KlR6UPDV9syR55l51955lNeWM/4Og74iv2MWLKPdKBuvPavql9LxvwQQ5z1IRf0f
# aGXBf1mZacAiMQxibqdcZQEhsGPEIhgn7ub80gA9Ry6ouIZWXQTcExclbhzfRA8V
# zbfbpVd2Qm8AaIKZ0uPB3vCLlFdM7AiQIiHOIiuYDELmQpOUmJPv/QbZP7xbm1Q8
# ILHuatZHesWrgOkwmt7xpD9VTQoJNIp1KdJprZcPUL/4ygkCAwEAAaOB9DCB8TAf
# BgNVHSMEGDAWgBTa7WR0FJwUPKvdmam9WyhNizzJ2DAdBgNVHQ4EFgQUjmstM2v0
# M6eTsxOapeAK9xI1aogwDgYDVR0PAQH/BAQDAgbAMAwGA1UdEwEB/wQCMAAwFgYD
# VR0lAQH/BAwwCgYIKwYBBQUHAwgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2Ny
# bC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNybDA1BggrBgEF
# BQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20w
# DQYJKoZIhvcNAQEFBQADggEBALozJEBAjHzbWJ+zYJiy9cAx/usfblD2CuDk5oGt
# Joei3/2z2vRz8wD7KRuJGxU+22tSkyvErDmB1zxnV5o5NuAoCJrjOU+biQl/e8Vh
# f1mJMiUKaq4aPvCiJ6i2w7iH9xYESEE9XNjsn00gMQTZZaHtzWkHUxY93TYCCojr
# QOUGMAu4Fkvc77xVCf/GPhIudrPczkLv+XZX4bcKBUCYWJpdcRaTcYxlgepv84n3
# +3OttOe/2Y5vqgtPJfO44dXddZhogfiqwNGAwsTEOYnB9smebNd0+dmX+E/CmgrN
# Xo/4GengpZ/E8JIh5i15Jcki+cPwOoRXrToW9GOUEB1d0MYwggV3MIIEX6ADAgEC
# AhAT6ihwW/Ts7Qw2YwmAYUM2MA0GCSqGSIb3DQEBDAUAMG8xCzAJBgNVBAYTAlNF
# MRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJu
# YWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJv
# b3QwHhcNMDAwNTMwMTA0ODM4WhcNMjAwNTMwMTA0ODM4WjCBiDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4w
# HAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVz
# dCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzO
# iZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwW
# IJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YU
# VD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1da
# t//O+T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+
# UzeQc0PzMsNT79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/z
# JSZrM233bkf6c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLa
# qUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxb
# gtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9A
# qURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+
# eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwID
# AQABo4H0MIHxMB8GA1UdIwQYMBaAFK29mHo0tCb3+sQmVO8DveAky1QaMB0GA1Ud
# DgQWBBRTeb9aqitKz1SA4dibwJ3ysgNmyzAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T
# AQH/BAUwAwEB/zARBgNVHSAECjAIMAYGBFUdIAAwRAYDVR0fBD0wOzA5oDegNYYz
# aHR0cDovL2NybC51c2VydHJ1c3QuY29tL0FkZFRydXN0RXh0ZXJuYWxDQVJvb3Qu
# Y3JsMDUGCCsGAQUFBwEBBCkwJzAlBggrBgEFBQcwAYYZaHR0cDovL29jc3AudXNl
# cnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAQEAk2X2N4OVD17Dghwf1nfnPIrA
# qgnw6Qsm8eDCanWhx3nJuVJgyCkSDvCtA9YJxHbf5aaBladG2oJXqZWSxbaPAyJs
# M3fBezIXbgfOWhRBOgUkG/YUBjuoJSQOu8wqdd25cEE/fNBjNiEHH0b/YKSR4We8
# 3h9+GRTJY2eR6mcHa7SPi8BuQ33DoYBssh68U4V93JChpLwt70ZyVzUFv7tGu25t
# N5m2/yOSkcZuQPiPKVbqX9VfFFOs8E9h6vcizKdWC+K4NB8m2XsZBWg/ujzUOAai
# 0+aPDuO0cW1AQsWEtECVK/RloEh59h2BY5adT3Xg+HzkjqnR8q2Ks4zHIc3C7zCC
# BawwggSUoAMCAQICEHJNXiAT1cKRQFXzfFSJVHEwDQYJKoZIhvcNAQELBQAwfDEL
# MAkGA1UEBhMCVVMxCzAJBgNVBAgTAk1JMRIwEAYDVQQHEwlBbm4gQXJib3IxEjAQ
# BgNVBAoTCUludGVybmV0MjERMA8GA1UECxMISW5Db21tb24xJTAjBgNVBAMTHElu
# Q29tbW9uIFJTQSBDb2RlIFNpZ25pbmcgQ0EwHhcNMTcxMjE0MDAwMDAwWhcNMjAx
# MjEzMjM1OTU5WjCByzELMAkGA1UEBhMCVVMxDjAMBgNVBBEMBTU1NDU1MRIwEAYD
# VQQIDAlNaW5uZXNvdGExFDASBgNVBAcMC01pbm5lYXBvbGlzMRgwFgYDVQQJDA8x
# MDAgVW5pb24gU3QgU0UxIDAeBgNVBAoMF1VuaXZlcnNpdHkgb2YgTWlubmVzb3Rh
# MSQwIgYDVQQLDBtDb21wdXRlciBhbmQgRGV2aWNlIFN1cHBvcnQxIDAeBgNVBAMM
# F1VuaXZlcnNpdHkgb2YgTWlubmVzb3RhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
# MIIBCgKCAQEAwk6kLE9u+tWv0JUkIJSn5pWfa09g6cqFLucCXomNj9NYj8t+JfPn
# a3gC6LHv3OQAUDHOoC5H+8N3ea7qVGYIiwPRHzXOGqG/tVaiU5s5hG3vBhfRX8W1
# /2g4/hpgeXUzrxYn/2c5SOGGy0MU1ZJyUSFEdsjXHEV7HXK4qmFGV9RJxtiLZH1q
# UldCglxcj7zw0QnUdG6oAxpwTCeVp057/WXbnIR8a0gPse+y/new5+CBUGTAvrw6
# K2BrJQVsdIIVn/q+BbcZxh9PpeZfTtsi6lgkvy0bUWtl5sSpd75+hvw4Sl3HAaWZ
# toWN7LPmbDbbVRO2Arv4doh4Chod4wJ5xQIDAQABo4IB2DCCAdQwHwYDVR0jBBgw
# FoAUrjUjF///Bj2cUOCMJGUzHnAQiKIwHQYDVR0OBBYEFF4LEhElVUvT8n5txOJS
# NAczooSAMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG
# CCsGAQUFBwMDMBEGCWCGSAGG+EIBAQQEAwIEEDBmBgNVHSAEXzBdMFsGDCsGAQQB
# riMBBAMCATBLMEkGCCsGAQUFBwIBFj1odHRwczovL3d3dy5pbmNvbW1vbi5vcmcv
# Y2VydC9yZXBvc2l0b3J5L2Nwc19jb2RlX3NpZ25pbmcucGRmMEkGA1UdHwRCMEAw
# PqA8oDqGOGh0dHA6Ly9jcmwuaW5jb21tb24tcnNhLm9yZy9JbkNvbW1vblJTQUNv
# ZGVTaWduaW5nQ0EuY3JsMH4GCCsGAQUFBwEBBHIwcDBEBggrBgEFBQcwAoY4aHR0
# cDovL2NydC5pbmNvbW1vbi1yc2Eub3JnL0luQ29tbW9uUlNBQ29kZVNpZ25pbmdD
# QS5jcnQwKAYIKwYBBQUHMAGGHGh0dHA6Ly9vY3NwLmluY29tbW9uLXJzYS5vcmcw
# GQYDVR0RBBIwEIEOb2l0bXB0QHVtbi5lZHUwDQYJKoZIhvcNAQELBQADggEBAENR
# lesMKmBaZ0g68lttYEMtaPiz+DaNpOlXBs1gH66aghB1aP6iiRJcFVasGLUVFncd
# G1xbw503LTrBUc5PECMVDVF7KKCfHA1OeFV9vOWyvdVgbe3paDy1sj4CADO2D0gn
# xcGiZoFhEZiBkTvSsj4S3GXZEvoFHJxJLw2kvdLnzy0gH/b/b/yblwA1fKXw4loc
# UpDM6qTwM7SiKgkQ5W7/280EYu8BI6c8rpiJmqM1tZLcpswuavB00T52Y+ZZmz3t
# MMVgFHn9pFFltYr3s3bEek7I6pU8unISbiyQzxqhIUKaBi8hy8LgoY5UnGjX5jHs
# IvINzms+JX5Ity02sL0wggXrMIID06ADAgECAhBl4eLj1d5QRYXzJiSABeLUMA0G
# CSqGSIb3DQEBDQUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNl
# eTEUMBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1Qg
# TmV0d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IFJTQSBDZXJ0aWZpY2F0aW9uIEF1
# dGhvcml0eTAeFw0xNDA5MTkwMDAwMDBaFw0yNDA5MTgyMzU5NTlaMHwxCzAJBgNV
# BAYTAlVTMQswCQYDVQQIEwJNSTESMBAGA1UEBxMJQW5uIEFyYm9yMRIwEAYDVQQK
# EwlJbnRlcm5ldDIxETAPBgNVBAsTCEluQ29tbW9uMSUwIwYDVQQDExxJbkNvbW1v
# biBSU0EgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
# CgKCAQEAwKAvix56u2p1rPg+3KO6OSLK86N25L99MCfmutOYMlYjXAaGlw2A6O2i
# gTXrC/Zefqk+aHP9ndRnec6q6mi3GdscdjpZh11emcehsriphHMMzKuHRhxqx+85
# Jb6n3dosNXA2HSIuIDvd4xwOPzSf5X3+VYBbBnyCV4RV8zj78gw2qblessWBRyN9
# EoGgwAEoPgP5OJejrQLyAmj91QGr9dVRTVDTFyJG5XMY4DrkN3dRyJ59UopPgNwm
# ucBMyvxR+hAJEXpXKnPE4CEqbMJUvRw+g/hbqSzx+tt4z9mJmm2j/w2nP35MViPW
# Cb7hpR2LB8W/499Yqu+kr4LLBfgKCQIDAQABo4IBWjCCAVYwHwYDVR0jBBgwFoAU
# U3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFK41Ixf//wY9nFDgjCRlMx5w
# EIiiMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMBMGA1UdJQQM
# MAoGCCsGAQUFBwMDMBEGA1UdIAQKMAgwBgYEVR0gADBQBgNVHR8ESTBHMEWgQ6BB
# hj9odHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVNFUlRydXN0UlNBQ2VydGlmaWNh
# dGlvbkF1dGhvcml0eS5jcmwwdgYIKwYBBQUHAQEEajBoMD8GCCsGAQUFBzAChjNo
# dHRwOi8vY3J0LnVzZXJ0cnVzdC5jb20vVVNFUlRydXN0UlNBQWRkVHJ1c3RDQS5j
# cnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZI
# hvcNAQENBQADggIBAEYstn9qTiVmvZxqpqrQnr0Prk41/PA4J8HHnQTJgjTbhuET
# 98GWjTBEE9I17Xn3V1yTphJXbat5l8EmZN/JXMvDNqJtkyOh26owAmvquMCF1pKi
# QWyuDDllxR9MECp6xF4wnH1Mcs4WeLOrQPy+C5kWE5gg/7K6c9G1VNwLkl/po9OR
# PljxKKeFhPg9+Ti3JzHIxW7LdyljffccWiuNFR51/BJHAZIqUDw3LsrdYWzgg4x0
# 6tgMvOEf0nITelpFTxqVvMtJhnOfZbpdXZQ5o1TspxfTEVOQAsp05HUNCXyhznlV
# Lr0JaNkM7edgk59zmdTbSGdMq8Ztuu6VyrivOlMSPWmay5MjvwTzuNorbwBv0DL+
# 7cyZBp7NYZou+DoGd1lFZN0jU5IsQKgm3+00pnnJ67crdFwfz/8bq3MhTiKOWEb0
# 4FT3OZVp+jzvaChHWLQ8gbCORgClaZq1H3aqI7JeRkWEEEp6Tv4WAVsr/i7LoXU7
# 2gOb8CAzPFqwI4Excdrxp0I4OXbECHlDqU4sTInqwlMwofmxeO4u94196qIqJQl+
# 8Sykl06VktqMux84Iw3ZQLH08J8LaJ+WDUycc4OjY61I7FGxCDkbSQf3npXeRFm0
# IBn8GiW+TRDk6J2XJFLWEtVZmhboFlBLoUlqHUCKu0QOhU/+AEOqnY98j2zRMYIE
# eTCCBHUCAQEwgZAwfDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk1JMRIwEAYDVQQH
# EwlBbm4gQXJib3IxEjAQBgNVBAoTCUludGVybmV0MjERMA8GA1UECxMISW5Db21t
# b24xJTAjBgNVBAMTHEluQ29tbW9uIFJTQSBDb2RlIFNpZ25pbmcgQ0ECEHJNXiAT
# 1cKRQFXzfFSJVHEwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKEC
# gAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwG
# CisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFLKbDHOAgWFdfQGzWUDFu1ELHqiV
# MA0GCSqGSIb3DQEBAQUABIIBAJxZVJxMLux0DuWwgD2+9UQpyH1X4usy91tLvgLn
# Ty0Flobvyx6LzDZ3XpiecnopUKullaO1rvS/A0mbDRuOJVVxZYaPJHsk8z8XJGDe
# YjL1KeanOhAQQ3AtXh7McT8kCbxhxjdlRmct0SH89HmC1OiKJtIRc7uJr7VUmdSw
# F+OHOiZEYUKDvSB4leeKpgz1m4TMrY6pM9F4hOPBSH9139xBahdqKYi/PqsOXTLq
# S5Gr8U8rg1cu9rvdaRru7QEUTr4vcLr4fItfH44EbSnOf3gRYMjzYPEZuM+nx36w
# wnyMOQHo4T8lrbAqsjM5+BM7MSAibvUvfnbiOihfGUXC8e2hggJDMIICPwYJKoZI
# hvcNAQkGMYICMDCCAiwCAQEwgakwgZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJV
# VDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJV
# U1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR0w
# GwYDVQQDExRVVE4tVVNFUkZpcnN0LU9iamVjdAIPFojwOSVeY45pFDkH5jMLMAkG
# BSsOAwIaBQCgXTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJ
# BTEPFw0xODAyMTYxOTE3MjFaMCMGCSqGSIb3DQEJBDEWBBSWiAY4cRG2LFCKyOdc
# gba/kqaywTANBgkqhkiG9w0BAQEFAASCAQAE11GgBjuNRUuINZVnjzd992R79Kma
# 7Qh6g+5Sw9WsTjmI0CC+mHwLcs7J7x4urqzK+mON1caNcLfik99A4ltG8DwpYWx0
# iVM2DPNZXTJN4sqk3vLL4HDc0PJYaAH9JuMJzL6/mP/47vtUd6YKN13uE9O+rKTI
# 9gmnX54IWa4BcQgTbw6s7dEO2P4NhbBQcEYFaL2xKfEISoqg3M7h8KdiVBqV3go8
# fyK95fDXZvLsutxbIJmrZDyCW4S5zTnrbE5EK+m6Ltdqltk4Hm3HF5y7D5BoVVk0
# 3cqMKhSSsnLTq/EfogAr9/KEJBrHVdUfZtx29j5iAVlEsXqf3b/vqoRj
# SIG # End signature block