AzurePSDrive.psm1

using namespace Microsoft.PowerShell.SHiPS
using module .\AzurePSDriveResource.psm1
using module .\AzurePSDriveStorageAccount.psm1
using module .\AzurePSDriveVM.psm1
using module .\AzurePSDriveWebApp.psm1

$script:AzureRM_Profile = if($IsCoreCLR){'AzureRM.Profile.NetCore'}else{'AzureRM.Profile'}
$script:AzureRM_Resources = if($IsCoreCLR){'AzureRM.Resources.Netcore'}else{'AzureRM.Resources'}

# Automatically pick resource group when inside resourcegroups of Azure drive
$Global:PSDefaultParameterValues['*-AzureRM*:ResourceGroupName'] = {if($pwd -like 'Azure:\*\ResourceGroups\*'){($pwd -split '\\')[3]}}

[SHiPSProvider(UseCache=$true)]
class Azure : SHiPSDirectory
{
    Azure([string]$name): base($name)
    {        
    } 

    [object[]] GetChildItem()
    {
        $obj =  @()  
        
        $defaultTenantId = $null      
        
        # CloudShell provides us with a default directory for Azure -> that maps to a tenant
        # This is provided in the form of tenantId in env variable ACC_TID
        if (-not $env:ACC_TID)
        {
            # Default tenantId not provided (perhaps provider is being run standalone => not in cloudshell)
            $tenant = (& "$script:AzureRM_Profile\Get-AzureRmTenant")

            if (($tenant -eq $null) -or ($tenant.Count -eq 0))
            {
                throw ('Unable to obtain tenant for the account. Check your subscription to ensure there is atleast one tenant')
            }

            # Use the first tenant, since this maps to the default directory chosen by the user via Portal
            $defaultTenantId = $tenant[0].Id
            Write-Verbose "Using TenantId '$($tenant[0].TenantId)'"                
            Write-Verbose "To change default tenant: Use AzureRM.profile\Get-AzureRmTenant to retrieve your tenants corresponding to directories and set environment variable 'ACC_TID' to desired tenant"
            Write-Verbose "Reload AzurePSDrive provider OR use 'dir -force' when navigating the subscription"
        }
        else
        {
            Write-Verbose "Using TenantId '$($env:ACC_TID)' from 'ACC_TID' environment variable..."
            $defaultTenantId = $env:ACC_TID
        }
        
        $subscriptions = $((& "$script:AzureRM_Profile\Get-AzureRmSubscription" -TenantId $defaultTenantId) | Sort-Object -Property Name)
        $subGroup = $subscriptions | Group-Object -Property Name

        foreach ($subscription in $subscriptions)
        {         
            
            $obj += [Subscription]::new($subscription.Name, $subscription.Name, $subscription.Id, $subscription.TenantId, $subscription.State)
            
        }

        return $obj;
    }
}

[SHiPSProvider(UseCache=$true)]
class Subscription : SHiPSDirectory
{
    [string]$SubscriptionName = $null
    [string]$SubscriptionId = $null
    [string]$TenantId = $null
    [string]$State = $null
            
    Subscription ([string]$subNameWithTenantId, [string]$subName, [string]$subId, [string]$tenantId, [string]$state) : base ($subNameWithTenantId)
    {
        $this.SubscriptionName = $subName
        $this.SubscriptionId = $subId
        $this.TenantId = $tenantId
        $this.State = $state
    }    

    [object[]] GetChildItem()
    {        
        & "$script:AzureRM_Profile\Select-AzureRmSubscription" -SubscriptionName $this.SubscriptionName -TenantId $this.TenantId
        $obj =  @()

        $obj+=[AllResources]::new();
        $obj+=[ResourceGroups]::new("ResourceGroups", $this.SubscriptionName, $this.SubscriptionId, $this.TenantId, $this.State)
        $obj+=[StorageAccounts]::new();
        $obj+=[VirtualMachines]::new();
        $obj+=[WebApps]::new();

        return $obj;
    }
 }
[SHiPSProvider(UseCache=$true)]
class ResourceGroups : SHiPSDirectory
{
    [string]$SubscriptionName = $null
    [string]$SubscriptionId = $null
    [string]$TenantId = $null
    [string]$State = $null
    [object[]]$rgs

    ResourceGroups ([string]$name, [string]$subName, [string]$subId, [string]$tenantId, [string]$state) : base ($name)
    {
        $this.SubscriptionName = $subName
        $this.SubscriptionId = $subId
        $this.TenantId = $tenantId
        $this.State = $state
    }


    [object[]] GetChildItem()
    {
        #AzureRM.profile\Select-AzureRmSubscription -SubscriptionName $this.SubscriptionName -TenantId $this.TenantId
        $obj =  @()
        $subId = $this.SubscriptionId

        $azureRMResourceParams = @{'ResourceId'="/subscriptions/$subId/resourceGroups"}

        if ($this.ProviderContext.Filter)
        {
            $azureRMResourceParams += @{'ODataQuery'=(Get-ODataQueryFilter -filter if($this.ProviderContext.Filter))}
        }

        @(& "$script:AzureRM_Resources\Get-AzureRmResource" @azureRMResourceParams).Foreach{
             
            $obj +=  [ResourceGroup]::new($subId, $_.Name, $_.Location, $_.Properties.ProvisioningState);                  
        }

        return $obj;
    }
 }

[SHiPSProvider(UseCache=$true)]
class ResourceGroup : SHiPSDirectory
{
    [string]$SubscriptionId = $null
    [string]$ResourceGroupName = $null
    [string]$Location = $null
    [string]$ProvisioningState = $null
        
    ResourceGroup ([string]$subscriptionId, [string]$name, [string]$location, [string]$provisioningState) : base ($name) 
    {
        $this.SubscriptionId = $subscriptionId
        $this.ResourceGroupName = $name
        $this.Location = $location
        $this.ProvisioningState = $provisioningState
    }

    [object[]] GetChildItem()
    {        
        $obj =  @()

        $resourceTypes = @(& "$script:AzureRM_Resources\Get-AzureRmResource" | Where-Object {$_.ResourceGroupName -eq $this.ResourceGroupName}  | select-Object -Property ResourceType -Unique).ForEach{$_.ResourceType.Split('/')[0]} | Select-Object -Unique
        foreach ($resourceType in $resourceTypes)
        {            
            $tempObj = [ResourceProvider]::new($resourceType, $this.ResourceGroupName);            
            
            $obj +=  $tempObj
        }
        
        return $obj; 
    }
 }

[SHiPSProvider(UseCache=$true)]
class ResourceProvider : SHiPSDirectory
{
    [string]$providerNamespace = $null
    [string]$resourceGroupName = $null

    ResourceProvider([string]$name): base($name)
    {
    }

    ResourceProvider ([string]$name, [string]$resourceGroupName) : base ($name) 
    {
        $this.providerNamespace = $name
        $this.resourceGroupName = $resourceGroupName
    }

    [object[]] GetChildItem()
    {        
        $obj =  @()

        $resourceTypeTokens = @()
        @(& "$script:AzureRM_Resources\Get-AzureRmResource" | Where-Object {$_.ResourceGroupName -eq $this.resourceGroupName} | Select-Object -Property ResourceType â€“Unique).ForEach{

            $providerNS = $_.ResourceType.Split('/')[0]
            $resourceType = $_.ResourceType.Substring($_.ResourceType.IndexOf('/')+1)
            $resourceType = $resourceType.Replace('/','-')
            if ($this.providerNamespace -eq $providerNS)
            {
                    $resourceTypeTokens += $resourceType
            }
        }

        foreach ($resourceTypeToken in ($resourceTypeTokens | Select-Object -Unique))
        {   
            $tempObj =  [ResourceType]::new($resourceTypeToken, $this.providerNamespace, $this.resourceGroupName);             

            $obj += $tempObj
        }        

        return $obj; 
    }
 }

[SHiPSProvider(UseCache=$true)]
class ResourceType : SHiPSDirectory
{
    [string]$resourceTypeName = $null
    [string]$resourceType = $null
    [string]$resourceGroupName = $null
    [string]$providerNamespace = $null
    [object]$Properties = $null

    ResourceType([string]$name): base($name)
    {
    }

    ResourceType ([string]$name, [string]$providerNamespace, [string]$resourceGroupName) : base ($name) 
    {   
        $this.resourceTypeName = $name
        $this.resourceGroupName = $resourceGroupName 
        $this.providerNamespace = $providerNamespace 
        $this.resourceType = $providerNamespace + '/' + $this.resourceTypeName.Replace('-', '/')
    }

    [object[]] GetChildItem()
    {        
        $obj =  @()

        $azureRMResourceParams = @{'ResourceGroupName'="$($this.resourceGroupName)"}
        $azureRMResourceParams += @{'ResourceType'="$($this.resourceType)"}
        $azureRMResourceParams += @{'ExpandProperties'=$true}

        if ($this.ProviderContext.Filter)
        {
            $azureRMResourceParams += @{'ODataQuery'=(Get-ODataQueryFilter -filter $this.ProviderContext.Filter)}
        }
        
        @(& "$script:AzureRM_Resources\Get-AzureRmResource" @azureRMResourceParams).Foreach{
                    
            if ($_.PSTypeNames.Contains('Microsoft.Network.networkSecurityGroups'))
            {   
                $typeName = $_.PSTypeNames[$_.PSTypeNames.IndexOf('Microsoft.Network.networkSecurityGroups')]

                foreach ($securityRule in $_.Properties.securityRules)
                {
                    $tempObj = $securityRule

                    1..2 | ForEach-Object {$tempObj.PSTypeNames.RemoveAt(0)}
                    $tempObj.PSTypeNames.Insert(0, $typeName + '.Rules')
                    
                    $obj += $tempObj
                }

                foreach ($defaultSecurityRule in $_.Properties.defaultSecurityRules)
                {
                    $tempObj = $defaultSecurityRule
                    
                    1..2 | ForEach-Object {$tempObj.PSTypeNames.RemoveAt(0)}            
                    $tempObj.PSTypeNames.Insert(0, $typeName + '.Rules')
                                        
                    $obj += $tempObj
                }
            }
            elseif ($_.PSTypeNames.Contains('Microsoft.Network.routeTables'))
            {                
                $typeName = $_.PSTypeNames[$_.PSTypeNames.IndexOf('Microsoft.Network.routeTables')]

                foreach ($route in $_.Properties.routes)
                {
                    $tempObj = $route

                    1..2 | ForEach-Object {$tempObj.PSTypeNames.RemoveAt(0)}
                    $tempObj.PSTypeNames.Insert(0, $typeName + '.routes')      

                    $obj += $tempObj
                }                

            }
            else

            {     
                $tempObj = $_
                1..2 | ForEach-Object {$tempObj.PSTypeNames.RemoveAt(0)}                                           
                $obj += $tempObj            
            }
        }
        
        return $obj; 

    }
 }

 #region Utilities

 # Given the filter string, return corresponding OData query filter in the format '$filter=<Name> <operator> <Value>'
 # Else, return null for invalid cases
 function Get-ODataQueryFilter
 {
    [OutputType([string])]
    param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $filter
    )
    

    if ('*' -eq $filter)
    {
        return $null
    }

    if ($filter.Contains('*'))
    {
        if ($filter.StartsWith('*') -and $filter.EndsWith('*'))
        {
            return "`$filter=substringof(Name, $filter.Replace('*', '')) eq true"
        }
        elseif ($dynamicParameters.Filter.StartsWith('*') -and (-not $dynamicParameters.Filter.EndsWith('*')))
        {            
            return "`$filter=EndsWith(Name, $filter.Replace('*', ''))"
        }
        elseif ($dynamicParameters.Filter.EndsWith('*') -and (-not $dynamicParameters.Filter.StartsWith('*')))
        {            
            return "`$filter=StartsWith(Name, $filter.Replace('*', ''))"
        }
        else
        {
            $filterTokens = $filter.Split('*')
            return "`$filter=StartsWith(Name, $filterTokens[0])"
        }
    }

    return $null
 }

 #endregion
# SIG # Begin signature block
# MIIarQYJKoZIhvcNAQcCoIIanjCCGpoCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUkaOl5RFkCO+iz4t4f8mZ1+Lf
# w6SgghWAMIIEwjCCA6qgAwIBAgITMwAAAL6kD/XJpQ7hMAAAAAAAvjANBgkqhkiG
# 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw
# HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTYwOTA3MTc1ODQ5
# WhcNMTgwOTA3MTc1ODQ5WjCBsjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjEMMAoGA1UECxMDQU9DMScwJQYDVQQLEx5uQ2lwaGVyIERTRSBFU046
# ODQzRC0zN0Y2LUYxMDQxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl
# cnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCROfFjRVxKmgTC
# tN14U6jxq1vAK7TBi39qS2BIU56Xw1IeOFNjg7bw6O8DMLr04Ghia8ath6lj3yab
# PSyXiYULrfk/7PkLUAqDbr6CFA/kuvoLYmePEgYKgI2vtruq05MABGYyw4WpUfLt
# chCNiBYWawyrdeHaw80xvfUrb7cDAU8st94bIkgyboaDN7f3oIzQHqyxok8XSSaZ
# JKTyqNtEtDo7p6ZJ3ygCa98lCk/SjpVnLkGlX0lJ3y/H2FM28gNnfQZQO8Pe0ICv
# 3KCpi4CPqx9LEuPgQoJrYK573I1LJlbjTV+l73UHPbo2w40W9L1SGu5UWrwNb6tZ
# qk4RwEvJAgMBAAGjggEJMIIBBTAdBgNVHQ4EFgQUHG4NXaJsQp0+3x29Li7nwpc0
# kH8wHwYDVR0jBBgwFoAUIzT42VJGcArtQPt2+7MrsMM1sw8wVAYDVR0fBE0wSzBJ
# oEegRYZDaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv
# TWljcm9zb2Z0VGltZVN0YW1wUENBLmNybDBYBggrBgEFBQcBAQRMMEowSAYIKwYB
# BQUHMAKGPGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z
# b2Z0VGltZVN0YW1wUENBLmNydDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG
# 9w0BAQUFAAOCAQEAbmBxbLeCqxsZFPMYFz/20DMP8Q12dH/1cNQursRMH0Yg0cTw
# Ln1IF3DGypfHZJwbyl9HWNVf+2Jq05zMajfjxiEu+khzmMnA9/BJ1utPwR0nPyyL
# bN+0IGBMfYLeIAdC81e0CW9TpWpc6lH/jgWbhviUt4Mvt2DQMWIQ7WwJAdBeGjCn
# tLINPxC9RmHysFGexMsXS+hYNR2z/h/PmvsNwhq7CtM6bM71ZvYFaBSCmtdQ8/KQ
# CPiN6acb2V/28VuZEwjq3GFAJfcKMvhssewRgCYsKxhvWZHUkBrUxWnsvxNCOWPp
# enBiVSYl5nT9jBoVoTDChMITR35gr//DmhzXszCCBOswggPToAMCAQICEzMAAAF4
# JVq1zSPGX5UAAQAAAXgwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjEjMCEGA1UEAxMaTWljcm9zb2Z0IENvZGUgU2ln
# bmluZyBQQ0EwHhcNMTcwODExMjAxMTE1WhcNMTgwODExMjAxMTE1WjCBgjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEMMAoGA1UECxMDQU9DMR4w
# HAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUA
# A4IBDwAwggEKAoIBAQCZbh1TVaudsrIbXUPPB9c8S+E+dKSbskHKYlG6SGTH8jhT
# hpuvGiAO87F2b9GHVN+DvszaMkGy/xQgHaGEJLfpnb3kpakic7E0bjDHdG4KnHRb
# no/wfUkGLfS79o+cw//RY8Ck6yE+0czDBcxp0Gbw5JyGP+KFqvzRR/3Tv3nt/5x0
# 5ZnEOHYP+eDVikDvn/DH+oxxtiCfX3tkvtm/yX4eOb47YdmYKQjCgz2+Nil/lupY
# vU0QFIjvke3jshqQINDng/vO9ys2qA0ex/q5hlLKQTST99dGoM86Ge6F723ReToq
# KnGLN8kiCG7uNapOAIQrpCHZq96CVumiaA5ZvxU9AgMBAAGjggFgMIIBXDATBgNV
# HSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUjuhtD3FD7tk/RKloJFX05cpgLjcw
# UQYDVR0RBEowSKRGMEQxDDAKBgNVBAsTA0FPQzE0MDIGA1UEBRMrMjI5ODAzKzFh
# YmY5ZTVmLWNlZDAtNDJlNi1hNjVkLWQ5MzUwOTU5ZmUwZTAfBgNVHSMEGDAWgBTL
# EejK0rQWWAHJNy4zFha5TJoKHzBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3Js
# Lm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNDb2RTaWdQQ0FfMDgt
# MzEtMjAxMC5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY0NvZFNpZ1BDQV8wOC0zMS0y
# MDEwLmNydDANBgkqhkiG9w0BAQUFAAOCAQEAYnG/oHG/xgZYR8NAMHZ/vE9GM0e4
# 7YdhuTea2uY7pSGwM707wp8Wan0Fa6evK1PWfcd/XNOh2BpEv5o8RmKDoEsG0ECP
# 13Jug7cklfKreBVHQ+Djg43VVFLZpuo7aOAVK6wjlcnpPUtn+SfH9K0aM2FjDKVJ
# FW6XFKXBat5R+Zp6uOxWTxpSeMTeDC5zF6IY6ogR1uzU+9EQoRlAvkwX6po+exEL
# nMLr4++P+fqOxIU+PODIoB8ijClAqwwKvLlMPa3qlrNHt+LweTMu7lvGC/RA18wU
# zzXAeomuZ03blUw+bkOiVgWOk4S0RN7EnW7zjJV8gd/+G2dbToUi1cB/fTCCBbww
# ggOkoAMCAQICCmEzJhoAAAAAADEwDQYJKoZIhvcNAQEFBQAwXzETMBEGCgmSJomT
# 8ixkARkWA2NvbTEZMBcGCgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMk
# TWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEwMDgzMTIy
# MTkzMloXDTIwMDgzMTIyMjkzMloweTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
# b3Jwb3JhdGlvbjEjMCEGA1UEAxMaTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0Ew
# ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCycllcGTBkvx2aYCAgQpl2
# U2w+G9ZvzMvx6mv+lxYQ4N86dIMaty+gMuz/3sJCTiPVcgDbNVcKicquIEn08Gis
# TUuNpb15S3GbRwfa/SXfnXWIz6pzRH/XgdvzvfI2pMlcRdyvrT3gKGiXGqelcnNW
# 8ReU5P01lHKg1nZfHndFg4U4FtBzWwW6Z1KNpbJpL9oZC/6SdCnidi9U3RQwWfjS
# jWL9y8lfRjFQuScT5EAwz3IpECgixzdOPaAyPZDNoTgGhVxOVoIoKgUyt0vXT2Pn
# 0i1i8UU956wIAPZGoZ7RW4wmU+h6qkryRs83PDietHdcpReejcsRj1Y8wawJXwPT
# AgMBAAGjggFeMIIBWjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTLEejK0rQW
# WAHJNy4zFha5TJoKHzALBgNVHQ8EBAMCAYYwEgYJKwYBBAGCNxUBBAUCAwEAATAj
# BgkrBgEEAYI3FQIEFgQU/dExTtMmipXhmGA7qDFvpjy82C0wGQYJKwYBBAGCNxQC
# BAweCgBTAHUAYgBDAEEwHwYDVR0jBBgwFoAUDqyCYEBWJ5flJRP8KuEKU5VZ5KQw
# UAYDVR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9j
# cmwvcHJvZHVjdHMvbWljcm9zb2Z0cm9vdGNlcnQuY3JsMFQGCCsGAQUFBwEBBEgw
# RjBEBggrBgEFBQcwAoY4aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0
# cy9NaWNyb3NvZnRSb290Q2VydC5jcnQwDQYJKoZIhvcNAQEFBQADggIBAFk5Pn8m
# Rq/rb0CxMrVq6w4vbqhJ9+tfde1MOy3XQ60L/svpLTGjI8x8UJiAIV2sPS9MuqKo
# VpzjcLu4tPh5tUly9z7qQX/K4QwXaculnCAt+gtQxFbNLeNK0rxw56gNogOlVuC4
# iktX8pVCnPHz7+7jhh80PLhWmvBTI4UqpIIck+KUBx3y4k74jKHK6BOlkU7IG9KP
# cpUqcW2bGvgc8FPWZ8wi/1wdzaKMvSeyeWNWRKJRzfnpo1hW3ZsCRUQvX/TartSC
# Mm78pJUT5Otp56miLL7IKxAOZY6Z2/Wi+hImCWU4lPF6H0q70eFW6NB4lhhcyTUW
# X92THUmOLb6tNEQc7hAVGgBd3TVbIc6YxwnuhQ6MT20OE049fClInHLR82zKwexw
# o1eSV32UjaAbSANa98+jZwp0pTbtLS8XyOZyNxL0b7E8Z4L5UrKNMxZlHg6K3RDe
# ZPRvzkbU0xfpecQEtNP7LN8fip6sCvsTJ0Ct5PnhqX9GuwdgR2VgQE6wQuxO7bN2
# edgKNAltHIAxH+IOVN3lofvlRxCtZJj/UBYufL8FIXrilUEnacOTj5XJjdibIa4N
# XJzwoq6GaIMMai27dmsAHZat8hZ79haDJLmIz2qoRzEvmtzjcT3XAH5iR9HOiMm4
# GPoOco3Boz2vAkBq/2mbluIQqBC0N1AI1sM9MIIGBzCCA++gAwIBAgIKYRZoNAAA
# AAAAHDANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZImiZPyLGQBGRYDY29tMRkwFwYK
# CZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNyb3NvZnQgUm9vdCBD
# ZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcwNDAzMTI1MzA5WhcNMjEwNDAzMTMw
# MzA5WjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEwHwYD
# VQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwggEiMA0GCSqGSIb3DQEBAQUA
# A4IBDwAwggEKAoIBAQCfoWyx39tIkip8ay4Z4b3i48WZUSNQrc7dGE4kD+7Rp9FM
# rXQwIBHrB9VUlRVJlBtCkq6YXDAm2gBr6Hu97IkHD/cOBJjwicwfyzMkh53y9Gcc
# LPx754gd6udOo6HBI1PKjfpFzwnQXq/QsEIEovmmbJNn1yjcRlOwhtDlKEYuJ6yG
# T1VSDOQDLPtqkJAwbofzWTCd+n7Wl7PoIZd++NIT8wi3U21StEWQn0gASkdmEScp
# ZqiX5NMGgUqi+YSnEUcUCYKfhO1VeP4Bmh1QCIUAEDBG7bfeI0a7xC1Un68eeEEx
# d8yb3zuDk6FhArUdDbH895uyAc4iS1T/+QXDwiALAgMBAAGjggGrMIIBpzAPBgNV
# HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQjNPjZUkZwCu1A+3b7syuwwzWzDzALBgNV
# HQ8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwgZgGA1UdIwSBkDCBjYAUDqyCYEBW
# J5flJRP8KuEKU5VZ5KShY6RhMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAXBgoJ
# kiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290IENl
# cnRpZmljYXRlIEF1dGhvcml0eYIQea0WoUqgpa1Mc1j0BxMuZTBQBgNVHR8ESTBH
# MEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0
# cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEESDBGMEQGCCsGAQUF
# BzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jvc29m
# dFJvb3RDZXJ0LmNydDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG9w0BAQUF
# AAOCAgEAEJeKw1wDRDbd6bStd9vOeVFNAbEudHFbbQwTq86+e4+4LtQSooxtYrhX
# AstOIBNQmd16QOJXu69YmhzhHQGGrLt48ovQ7DsB7uK+jwoFyI1I4vBTFd1Pq5Lk
# 541q1YDB5pTyBi+FA+mRKiQicPv2/OR4mS4N9wficLwYTp2OawpylbihOZxnLcVR
# DupiXD8WmIsgP+IHGjL5zDFKdjE9K3ILyOpwPf+FChPfwgphjvDXuBfrTot/xTUr
# XqO/67x9C0J71FNyIe4wyrt4ZVxbARcKFA7S2hSY9Ty5ZlizLS/n+YWGzFFW6J1w
# lGysOUzU9nm/qhh6YinvopspNAZ3GmLJPR5tH4LwC8csu89Ds+X57H2146SodDW4
# TsVxIxImdgs8UoxxWkZDFLyzs7BNZ8ifQv+AeSGAnhUwZuhCEl4ayJ4iIdBD6Svp
# u/RIzCzU2DKATCYqSCRfWupW76bemZ3KOm+9gSd0BhHudiG/m4LBJ1S2sWo9iaF2
# YbRuoROmv6pH8BJv/YoybLL+31HIjCPJZr2dHYcSZAI9La9Zj7jkIeW1sMpjtHhU
# BdRBLlCslLCleKuzoJZ1GtmShxN1Ii8yqAhuoFuMJb+g74TKIdbrHk/Jmu5J4PcB
# ZW+JC33Iacjmbuqnl84xKf8OxVtc2E0bodj6L54/LlUWa8kTo/0xggSXMIIEkwIB
# ATCBkDB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSMwIQYD
# VQQDExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQQITMwAAAXglWrXNI8ZflQAB
# AAABeDAJBgUrDgMCGgUAoIGwMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwG
# CisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBTv3yvb
# V+Yvmhu4rvFkFNeB4zDOGjBQBgorBgEEAYI3AgEMMUIwQKAWgBQAUABvAHcAZQBy
# AFMAaABlAGwAbKEmgCRodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vUG93ZXJTaGVs
# bCAwDQYJKoZIhvcNAQEBBQAEggEAHxJOUQFdJwxTf7vYKq/197bz8HNcDmmiyxrY
# qVfY4inVYrU1M/7w4bU8qGAIk/DwCGv0Qt0yvWGQADnuRe9rrzJ/Ru/YUkz5dHsc
# ChIwBPJPd3IS47IVaD31yBdRvq73WSoIo4zhF3q3EyG//5IoP+NgP5dbr9tZISwz
# OYoV9c/M4WVprYzrKOavh5KBdsS8WzaU7dvg8NgCwI8sJY7VD7fFFAwrGJG6WL9d
# KgXT/XgRlCEl7SICWypTlvCi0KxR49UFHmEjNC4uwOzzN7VqUQNoKTLkYLo5iOaF
# zXBPFHsqGCw4kXq5kfQizdn/a1STaIz1+P7XQH/U0tLInzKcUqGCAigwggIkBgkq
# hkiG9w0BCQYxggIVMIICEQIBATCBjjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EC
# EzMAAAC+pA/1yaUO4TAAAAAAAL4wCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzEL
# BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE3MTAxNjIzMzU0NFowIwYJKoZI
# hvcNAQkEMRYEFKhRDqW6AJFdziX+5lBQ5ep0w1MSMA0GCSqGSIb3DQEBBQUABIIB
# ADUeN5po6bTZMB91NABRFezJ2ynMgZkbV+tF/OVvPROa/YO8xHaXexB0N+NOswwA
# 5DfWLozmW3hZlQDYX7XBhaElnqyxDGLPoFr0t2PCarOW/Xn3nLprKvrizwCbySx0
# q2Yzx7BXOe4fqOyyE5rAsSlAzqwTRyBYv/yW8Mq4mwPWt7t4lLZQPWS8SDKaARkB
# lXQ78URH2ie8S2gfyaO/mAjzgFu8cR7Ee5/n98QihWLHyqv+jPVtTc3izSJu5O3+
# uB9gP92neMf3WtNZZyFjqi7WTjXRv8FC7qkhz2rIP7+QY9vZNAhpCQDQv4otXaDX
# TcJudVvY5Qkj6feDTqkKu8s=
# SIG # End signature block