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'}
$script:pathPattern = [System.IO.Path]::Combine('Azure:', '*', 'ResourceGroups', '*')
$script:pathSeparator = if([System.IO.Path]::DirectorySeparatorChar -eq '\'){'\\'}else{'/'}

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

[SHiPSProvider(UseCache=$true)]
class Azure : SHiPSDirectory
{
    Azure([string]$name): base($name)
    {        
        # Ensure Session is logged-on to access Azure resources
        # This is done in the constructor so that it is a runtime check and not done during module import.
        $context = (& "$script:AzureRM_Profile\Get-AzureRmContext")
        if ([string]::IsNullOrEmpty($($context.Account)))
        {
            throw "Ensure that session has access to Azure resources - use $script:AzureRM_Profile\Add-AzureRMAccount or $script:AzureRM_Profile\Login-AzureRMAccount"
        }
    } 

    [object[]] GetChildItem()
    {
        $obj =  @()  
        
        $defaultTenantId = $null      
        
        # Cloud Shell 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 Cloud Shell)
            $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 at least 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

        @(& "$script:AzureRM_Resources\Get-AzureRmResourceGroup").Foreach{
             
            $obj +=  [ResourceGroup]::new($subId, $_.ResourceGroupName, $_.Location, $_.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
# MIIdhwYJKoZIhvcNAQcCoIIdeDCCHXQCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU38BKJWKt13ADatMwxjBrUnW1
# GvOgghhVMIIEwzCCA6ugAwIBAgITMwAAAMWWQGBL9N6uLgAAAAAAxTANBgkqhkiG
# 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw
# HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTYwOTA3MTc1ODUy
# WhcNMTgwOTA3MTc1ODUyWjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNO
# OkMwRjQtMzA4Ni1ERUY4MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# ZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtrwz4CWOpvnw
# EBVOe1crKElrs3CQl/yun1cdkpugh/MxsuoGn7BL43GRTxRn7sPD7rq1Dxj4smPl
# gVZr/ZhGMA8J3zXOqyIcD4hYFikXuhlGuuSokunCAxUl5N4gjN/M7+NwJPm2JtYK
# ZLBdH5J/y+GIk7rQhpgbstpLOZf4GHgC8Myji7089O1uX2MCKFFU+wt2Y560O4Xc
# 2NVjeuG+nnq5pGyq9111nK3f0DeT7FWjDVQWFghKOhyeBb4iMhmkdA8vWpYmx6TN
# c+d35nSZcLc0EhSIVJkzEBYfwkrzxFaG/pgNJ9C4jm/zHgwWLZwQpU7K2fP15fGk
# BGplwNjr1wIDAQABo4IBCTCCAQUwHQYDVR0OBBYEFA4B9X87yXgCWEZxOwn8mnVX
# hjjEMB8GA1UdIwQYMBaAFCM0+NlSRnAK7UD7dvuzK7DDNbMPMFQGA1UdHwRNMEsw
# SaBHoEWGQ2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3Rz
# L01pY3Jvc29mdFRpbWVTdGFtcFBDQS5jcmwwWAYIKwYBBQUHAQEETDBKMEgGCCsG
# AQUFBzAChjxodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jv
# c29mdFRpbWVTdGFtcFBDQS5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI
# hvcNAQEFBQADggEBAAUS3tgSEzCpyuw21ySUAWvGltQxunyLUCaOf1dffUcG25oa
# OW/WuIFJs0lv8Py6TsOrulsx/4NTkIyXra/MsJvwczMX2s/vx6g63O3osQI85qHD
# dp8IMULGmry+oqPVTuvL7Bac905EqqGXGd9UY7y14FcKWBWJ28vjncTw8CW876pY
# 80nSm8hC/38M4RMGNEp7KGYxx5ZgGX3NpAVeUBio7XccXHEy7CSNmXm2V8ijeuGZ
# J9fIMkhiAWLEfKOgxGZ63s5yGwpMt2QE/6Py03uF+X2DHK76w3FQghqiUNPFC7uU
# o9poSfArmeLDuspkPAJ46db02bqNyRLP00bczzwwggYBMIID6aADAgECAhMzAAAA
# xOmJ+HqBUOn/AAAAAADEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTEwHhcNMTcwODExMjAyMDI0WhcNMTgwODExMjAyMDI0WjB0
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
# AoIBAQCIirgkwwePmoB5FfwmYPxyiCz69KOXiJZGt6PLX4kvOjMuHpF4+nypH4IB
# tXrLGrwDykbrxZn3+wQd8oUK/yJuofJnPcUnGOUoH/UElEFj7OO6FYztE5o13jhw
# VG877K1FCTBJwb6PMJkMy3bJ93OVFnfRi7uUxwiFIO0eqDXxccLgdABLitLckevW
# eP6N+q1giD29uR+uYpe/xYSxkK7WryvTVPs12s1xkuYe/+xxa8t/CHZ04BBRSNTx
# AMhITKMHNeVZDf18nMjmWuOF9daaDx+OpuSEF8HWyp8dAcf9SKcTkjOXIUgy+MIk
# ogCyvlPKg24pW4HvOG6A87vsEwvrAgMBAAGjggGAMIIBfDAfBgNVHSUEGDAWBgor
# BgEEAYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUy9ZihM9gOer/Z8Jc0si7q7fD
# E5gwUgYDVR0RBEswSaRHMEUxDTALBgNVBAsTBE1PUFIxNDAyBgNVBAUTKzIzMDAx
# MitjODA0YjVlYS00OWI0LTQyMzgtODM2Mi1kODUxZmEyMjU0ZmMwHwYDVR0jBBgw
# FoAUSG5k5VAF04KqFzc3IrVtqMp1ApUwVAYDVR0fBE0wSzBJoEegRYZDaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljQ29kU2lnUENBMjAxMV8y
# MDExLTA3LTA4LmNybDBhBggrBgEFBQcBAQRVMFMwUQYIKwYBBQUHMAKGRWh0dHA6
# Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljQ29kU2lnUENBMjAx
# MV8yMDExLTA3LTA4LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IC
# AQAGFh/bV8JQyCNPolF41+34/c291cDx+RtW7VPIaUcF1cTL7OL8mVuVXxE4KMAF
# RRPgmnmIvGar27vrAlUjtz0jeEFtrvjxAFqUmYoczAmV0JocRDCppRbHukdb9Ss0
# i5+PWDfDThyvIsoQzdiCEKk18K4iyI8kpoGL3ycc5GYdiT4u/1cDTcFug6Ay67Sz
# L1BWXQaxFYzIHWO3cwzj1nomDyqWRacygz6WPldJdyOJ/rEQx4rlCBVRxStaMVs5
# apaopIhrlihv8cSu6r1FF8xiToG1VBpHjpilbcBuJ8b4Jx/I7SCpC7HxzgualOJq
# nWmDoTbXbSD+hdX/w7iXNgn+PRTBmBSpwIbM74LBq1UkQxi1SIV4htD50p0/GdkU
# ieeNn2gkiGg7qceATibnCCFMY/2ckxVNM7VWYE/XSrk4jv8u3bFfpENryXjPsbtr
# j4Nsh3Kq6qX7n90a1jn8ZMltPgjlfIOxrbyjunvPllakeljLEkdi0iHv/DzEMQv3
# Lz5kpTdvYFA/t0SQT6ALi75+WPbHZ4dh256YxMiMy29H4cAulO2x9rAwbexqSajp
# lnbIvQjE/jv1rnM3BrJWzxnUu/WUyocc8oBqAU+2G4Fzs9NbIj86WBjfiO5nxEmn
# L9wliz1e0Ow0RJEdvJEMdoI+78TYLaEEAo5I+e/dAs8DojCCBgcwggPvoAMCAQIC
# CmEWaDQAAAAAABwwDQYJKoZIhvcNAQEFBQAwXzETMBEGCgmSJomT8ixkARkWA2Nv
# bTEZMBcGCgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0
# IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA3MDQwMzEyNTMwOVoXDTIx
# MDQwMzEzMDMwOVowdzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEhMB8GA1UEAxMYTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBMIIBIjANBgkqhkiG
# 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn6Fssd/bSJIqfGsuGeG94uPFmVEjUK3O3RhO
# JA/u0afRTK10MCAR6wfVVJUVSZQbQpKumFwwJtoAa+h7veyJBw/3DgSY8InMH8sz
# JIed8vRnHCz8e+eIHernTqOhwSNTyo36Rc8J0F6v0LBCBKL5pmyTZ9co3EZTsIbQ
# 5ShGLieshk9VUgzkAyz7apCQMG6H81kwnfp+1pez6CGXfvjSE/MIt1NtUrRFkJ9I
# AEpHZhEnKWaol+TTBoFKovmEpxFHFAmCn4TtVXj+AZodUAiFABAwRu233iNGu8Qt
# VJ+vHnhBMXfMm987g5OhYQK1HQ2x/PebsgHOIktU//kFw8IgCwIDAQABo4IBqzCC
# AacwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUIzT42VJGcArtQPt2+7MrsMM1
# sw8wCwYDVR0PBAQDAgGGMBAGCSsGAQQBgjcVAQQDAgEAMIGYBgNVHSMEgZAwgY2A
# FA6sgmBAVieX5SUT/CrhClOVWeSkoWOkYTBfMRMwEQYKCZImiZPyLGQBGRYDY29t
# MRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNyb3NvZnQg
# Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCEHmtFqFKoKWtTHNY9AcTLmUwUAYD
# VR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwv
# cHJvZHVjdHMvbWljcm9zb2Z0cm9vdGNlcnQuY3JsMFQGCCsGAQUFBwEBBEgwRjBE
# BggrBgEFBQcwAoY4aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9N
# aWNyb3NvZnRSb290Q2VydC5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI
# hvcNAQEFBQADggIBABCXisNcA0Q23em0rXfbznlRTQGxLnRxW20ME6vOvnuPuC7U
# EqKMbWK4VwLLTiATUJndekDiV7uvWJoc4R0Bhqy7ePKL0Ow7Ae7ivo8KBciNSOLw
# UxXdT6uS5OeNatWAweaU8gYvhQPpkSokInD79vzkeJkuDfcH4nC8GE6djmsKcpW4
# oTmcZy3FUQ7qYlw/FpiLID/iBxoy+cwxSnYxPStyC8jqcD3/hQoT38IKYY7w17gX
# 606Lf8U1K16jv+u8fQtCe9RTciHuMMq7eGVcWwEXChQO0toUmPU8uWZYsy0v5/mF
# hsxRVuidcJRsrDlM1PZ5v6oYemIp76KbKTQGdxpiyT0ebR+C8AvHLLvPQ7Pl+ex9
# teOkqHQ1uE7FcSMSJnYLPFKMcVpGQxS8s7OwTWfIn0L/gHkhgJ4VMGboQhJeGsie
# IiHQQ+kr6bv0SMws1NgygEwmKkgkX1rqVu+m3pmdyjpvvYEndAYR7nYhv5uCwSdU
# trFqPYmhdmG0bqETpr+qR/ASb/2KMmyy/t9RyIwjyWa9nR2HEmQCPS2vWY+45CHl
# tbDKY7R4VAXUQS5QrJSwpXirs6CWdRrZkocTdSIvMqgIbqBbjCW/oO+EyiHW6x5P
# yZruSeD3AWVviQt9yGnI5m7qp5fOMSn/DsVbXNhNG6HY+i+ePy5VFmvJE6P9MIIH
# ejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UE
# BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc
# BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0
# IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5
# WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDEx
# MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00
# uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kN
# eWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/n
# qwhQz7NEt13YxC4Ddato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3V
# XHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6x
# jF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5k
# f1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4c
# I6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bys
# AoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexN
# STCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93
# KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX
# 3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEA
# MB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4K
# AFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME
# GDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRw
# Oi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJB
# dXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcw
# AoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJB
# dXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcu
# AzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w
# cy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEA
# bABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3
# DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74
# w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQ
# sP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6Sp
# BQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd
# 8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJx
# Jxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9
# Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEG
# sXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AA
# KcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/
# 1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EK
# sT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCBJww
# ggSYAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# KDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAADE
# 6Yn4eoFQ6f8AAAAAAMQwCQYFKw4DAhoFAKCBsDAZBgkqhkiG9w0BCQMxDAYKKwYB
# BAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0B
# CQQxFgQUw/0QYL/JUsxiBvA5pjl8cKGHEIgwUAYKKwYBBAGCNwIBDDFCMECgFoAU
# AFAAbwB3AGUAcgBTAGgAZQBsAGyhJoAkaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L1Bvd2VyU2hlbGwgMA0GCSqGSIb3DQEBAQUABIIBAIFfpq51cpElR/eKZuhVNZXb
# oZjwn2jnFRgzwqSQZ7B/XKTIKp8I/P0F/6uw7nTgPQyA6D1rZKEGygXGoIXyGl6Z
# 5TVicV7df2dU8l1/P9pMRJYN3ThiY9biZUYh7JShr6IGbtt53yrti/24sKXwGYYa
# sgA54oF7sz9TD8SxUt3Gz0We8hl3ZCpXvYbcdvE1rqMLdqAFU/jVa1cHBCHGKSgp
# 2rReRcCPCEvuEeufBxxfijnjou3u8Lf1FfAqRe3QKrXewmScdMFY+ljtFCKqBFhj
# HFfMloeuSN9te1knB51dSB75jVzBcYiIQCIbaCfyqDjPBpteNWgNQWKYjFGttVGh
# ggIoMIICJAYJKoZIhvcNAQkGMYICFTCCAhECAQEwgY4wdzELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEhMB8GA1UEAxMYTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgUENBAhMzAAAAxZZAYEv03q4uAAAAAADFMAkGBSsOAwIaBQCgXTAYBgkq
# hkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xODA2MTUxOTIy
# NTVaMCMGCSqGSIb3DQEJBDEWBBSqSnD/jwUwbVK37DG3BM/DG2E6sDANBgkqhkiG
# 9w0BAQUFAASCAQA0594ZdgjaQrY60m7V6xHa5IC30LL18qougLJ93QsO9Aawc35I
# EMow6YKwOI921FTv3/FRrhSdpwNEkIPEh4yomY69ZWuVqKHF66hFNcCTNrxm4IZ4
# BzUGM0XNOwzL1g0YEFGwNTS+DPHr8CEA/s+PluUSSPiJXNtncveaJvBnSpPTld9Q
# dAzWeTuij5q+isVlLwmlrpnGvfCFicQIQafNa+Ek9LHKeES6NdmGUyo8BZ4PeHW9
# sPJUJPNBrodS9vdF1SLvJrzrRy6ZA8P6lImbc9+3/gUanHrAQvCTqGA+YtaDzl4i
# fHVw18yUexEV5JgzqVRO9WUnA3AYalhcs/fi
# SIG # End signature block