Get-AzureAssessResources.ps1

<#
.SYNOPSIS
    Gets the resources to be analysed within specified scope
.EXAMPLE
    PS C:\Get-AzureAssessResourses -SubscriptionId 3395068f-a9b5-41a9-af54-bd362b69e19a -ResourceGroupName "rg-app-service"
    # Get all resource in indicated resourcegroup and subscription
.EXAMPLE
    PS C:\Get-AzureAssessResourses -SubscriptionId 3395068f-a9b5-41a9-af54-bd362b69e19a
    # Get all resource for all resource groups in subscription
.EXAMPLE
    PS C:\Get-AzureAssessResourse -MamagementGroup "myManagementGroup"
    # Get all ressource in all the subscriptions and resource groups under indicated management group
#>


$defenderforcloudchecks = @{
    "VirtualMachines" = @("Microsoft.Compute/virtualMachines", "Microsoft.HybridCompute/machines")
    "AppServices" = @("Microsoft.Web/sites")
    "SqlServers" = @("Microsoft.Sql/servers", "Microsoft.Sql/sqlManagedInstances", "Microsoft.AzureArcData/sqlServers", "Microsoft.AzureArcData/sqlManagedInstances")
    "SqlServerVirtualMachines" = @("Microsoft.SqlVirtualMachine/sqlVirtualMachines")
    "OpenSourceRelationalDatabases" = @("Microsoft.DBforMySQL/servers","Microsoft.DBforPostgreSQL/servers","Microsoft.DBforMariaDB/servers","Microsoft.AzureArcData/postgresInstances")
    "CosmosDbs" = @("Microsoft.DocumentDB/databaseAccounts") 
    "StorageAccounts" = @("Microsoft.Storage/storageAccounts")
    "ContainerRegistry" = @("Microsoft.ContainerRegistry/registries")
    "KubernetesService" = @("Microsoft.ContainerService/managedCluster", "Microsoft.ContainerService/connectedCluster")
    "Containers" = @("Microsoft.ContainerRegistry/registries", "Microsoft.ContainerService/managedCluster", "Microsoft.ContainerService/connectedCluster")
    "KeyVaults" = @("Microsoft.KeyVault/vaults")
    "DNS" = @()
    "Arm" = @()
}

function Get-AzureAssessResources() {
    [CmdletBinding(DefaultParameterSetName="SubnRg")]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 1, ParameterSetName="SubnRg")]
        [string]$SubscriptionId,
        [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 2, ParameterSetName="SubnRg")]
        [string]$ResourceGroupName,
        [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 2, ParameterSetName="MgmtGrp")]
        [string[]]$ManagementGroupNames,
        [Parameter(Mandatory = $false)]
        [validateSet("Microsoft.Web/sites", "Microsoft.Web/serverFarms", "Microsoft.Network/privateEndpoints","Microsoft.Storage/storageAccounts","Microsoft.Sql/servers","Microsoft.KeyVault/vaults","Microsoft.Compute/virtualMachines")]
        [string[]]$ResourceTypes = ("Microsoft.Web/sites", "Microsoft.Web/serverFarms", "Microsoft.Network/privateEndpoints","Microsoft.Storage/storageAccounts","Microsoft.Sql/servers","Microsoft.KeyVault/vaults","Microsoft.Compute/virtualMachines")
    )

    # Initialize CSV files for collected resources
    $privateendpointscsv = Join-Path -Path "." -ChildPath "privateendpoints.csv"
    $webappscsv = Join-Path -Path "." -ChildPath "webapps.csv"
    $appserviceplanscsv = Join-Path -Path "." -ChildPath "appserviceplans.csv"
    $storagecsv = Join-Path -Path "." -ChildPath "storages.csv"
    $sqlserverscsv = Join-Path -Path "." -ChildPath "sqlservers.csv"
    $keyvaultscsv = Join-Path -Path "." -ChildPath "keyvaults.csv"
    $virtualmachinescsv = Join-Path -Path "." -ChildPath "virtualmachines.csv"
    $managementgroupscsv = Join-Path -Path "." -ChildPath "managementgroups.csv"
    $subscriptionscsv = Join-Path -Path "." -ChildPath "subscriptions.csv"
    $resourcegroupscsv = Join-Path -Path "." -ChildPath "resourcegroups.csv"
    $securityrecommendationscsv = Join-Path -Path "." -ChildPath "securityrecommendations.csv"
    $roleassignmentscsv = Join-Path "." -ChildPath "roleassignments.csv"

    Write-Output "Initializing csv files"

    # Initialize CSV files with headers
    if ($ResourceTypes -contains "Microsoft.Network/privateEndpoints") {
        "Id,ResourceGroupId,Type,Name,Link,Location,ServiceSubscriptionId,ServiceResourceGroupName,ServiceProvider,ServiceName" | Out-File -FilePath $privateendpointscsv -Force
    }
    if ($ResourceTypes -contains "Microsoft.Web/sites") {
        "Id,ResourceGroupId,Type,Name,Link,Location,PublicNetworkAccess,HasFirewallRules,HasPrivateEndpoint,State,DefaultHostName,HttpsOnly,MinTlsVersion,FtpsState,AppServiceEnvironementInbound" | Out-File -FilePath $webappscsv -Force
    }
    if ($ResourceTypes -contains "Microsoft.Web/serverFarms") {
        "Id,ResourceGroupId,Type,Name,Link,Location,Sku,AppServiceEnvironementInbound" | Out-File -FilePath $appserviceplanscsv -Force
    }
    if ($ResourceTypes -contains "Microsoft.Storage/storageAccounts") {
        "Id,ResourceGroupId,Type,Name,Link,Location,PublicNetworkAccess,HasFirewallRules,HasPrivateEndpoint,Sku,MinimumTlsVersion,EnableHttpsTrafficOnly,AllowBlobPublicAccess,InfrastructureEncryption,ServiceEncryption" | Out-File -FilePath $storagecsv -Force
    }
    if ($ResourceTypes -contains "Microsoft.Sql/servers") {
        "Id,ResourceGroupId,Type,Name,Link,Location,PublicNetworkAccess,HasFirewallRules,HasPrivateEndpoint,FullyQualifiedDomainName,MinimalTlsVersion,AdministratorType,EntraOnlyAuth" | Out-File -FilePath $sqlserverscsv -Force
    }
    if ($ResourceTypes -contains "Microsoft.KeyVault/vaults") {
        "Id,ResourceGroupId,Type,Name,Link,Location,PublicNetworkAccess,HasFirewallRules,HasPrivateEndpoint,VaultUri,EnableSoftDelete,EnablePurgeProtection" | Out-File -FilePath $keyvaultscsv -Force
    }
    if ($ResourceTypes -contains "Microsoft.Compute/virtualMachines") {
        "Id,ResourceGroupId,Type,Name,Link,Location,PublicNetworkAccess,HasFirewallRules,HasPrivateEndpoint,HasOpenSSHorRDP,VmSize,PublicIp" | Out-File -FilePath $virtualmachinescsv -Force
    }
    "ResourceGroupId,ResourceId,ResourceType,ResourceName,Recommendation,Description,Severity" | Out-File -FilePath $securityrecommendationscsv -Force
    if ($PSCmdlet.ParameterSetName -eq "MgmtGrp") {
        "ManagementGroup" | Out-File -FilePath $managementgroupscsv -Force
    }
    "ManagementGroup,Id,Name,Link,DefenderForVirtualMachines,DefenderForAppServices,DefenderForSqlServers,DefenderForSqlServerVirtualMachines,DefenderForOpenSourceDBs,DefenderForCosmosDbs,DefenderForStorageAccounts,DefenderForContainerRegistry,DefenderForKubernetesService,DefenderForContainers,DefenderForKeyVaults,DefenderForDNS,DefenderForArm,HasVirtualMachines,HasAppServices,HasSqlServers,HasSqlServerVirtualMachines,HasOpenSourceDBs,HasCosmosDbs,HasStorageAccounts,HasContainerRegistry,HasKubernetesService,HasContainers,HasKeyVaults" | Out-File -FilePath $subscriptionscsv -Force
    "SubscriptionId,Id,Name,Link" | Out-File -FilePath $resourcegroupscsv -Force
    "roleId,role,principalId,principalType,scope,source,resourceType,resourceName,link" | Out-File -FilePath $roleassignmentscsv -Force

    Write-Output "Get subscriptions ids"

    $SubscriptionIds = @()
    $context = get-azcontext

    if ($PSCmdlet.ParameterSetName -eq "SubnRg") {
        $SubscriptionIds += "" | Select-Object @{N="ManagementGroup";E={$null}},@{N="SubscriptionId";E={$SubscriptionId}}
    } elseif ($PSCmdlet.ParameterSetName -eq "MgmtGrp") {
        foreach($ManagementGroupName in $ManagementGroupNames) {
            # Expand all management groups and subscriptions under the indicated management group
            $SubscriptionIds += Get-AzManagementGroupSubscription -GroupName $ManagementGroupName | Select-Object @{N="ManagementGroup";E={$ManagementGroupName}},@{N="SubscriptionId";E={($_.Id -split "/")[-1]}}
            "" | Select-Object @{N="ManagementGroup";E={$ManagementGroupName}} | Export-Csv -Path $managementgroupscsv -NoTypeInformation -Append
        }
    }

    # Message that collection is starting for the number of subscriptions
    Write-Output "Perparing collection for $($SubscriptionIds.Count) subscriptions" 

    # Expand all subscriptions and their ressource groups
    $collections = @()
    $resourcegroupinfos = @()
    $subscriptionsinfos = @()
    foreach($CurrentSubscription in $SubscriptionIds) {
        $ManagementGroupName = $CurrentSubscription.ManagementGroup
        $SubscriptionName = (Set-AzContext -Subscription $CurrentSubscription.SubscriptionId).Subscription.Name
        # get defender for cloud informations
        $defenderforcloud = @{}
        $defenderforcloudres = @{}
        $securitypricing = @(Get-AzSecurityPricing)
        foreach($defenderforcloudcheck in $defenderforcloudchecks.Keys) {
            $defenderforcloud[$defenderforcloudcheck] = ($securitypricing | Where-Object {$_.Name -eq $defenderforcloudcheck}).PricingTier -eq "Standard"
            # check resources presences
            $defenderforcloudres[$defenderforcloudcheck] = ($defenderforcloudchecks[$defenderforcloudcheck]).Count -eq 0
            if ($defenderforcloudres[$defenderforcloudcheck] -eq $false) {
                $jobs = @()
                foreach($defenderforcloudreskey in $defenderforcloudchecks[$defenderforcloudcheck]) {
                    $jobs += invoke-azrestmethod -AsJob -method "GET" -path "/subscriptions/$($CurrentSubscription.SubscriptionId)/resources?`$filter=resourceType+eq+'$defenderforcloudreskey'&`$top=1&api-version=2023-07-01" 
                }
                $res = $jobs | Wait-Job | Receive-Job | Where-Object { $_.StatusCode -eq 200 } | ForEach-Object { $_.Content |  ConvertFrom-Json } | Select-Object -ExpandProperty value
                $defenderforcloudres[$defenderforcloudcheck] = $res.Count -gt 0
            }
        }
        $subscriptionsinfos += "" | select-object `
            @{N="ManagementGroup";E={$ManagementGroupName}}, `
            @{N="Id";E={$CurrentSubscription.SubscriptionId}}, `
            @{N="Name";E={$SubscriptionName}}, `
            @{N="Link";E={"https://portal.azure.com/#@$($context.Tenant.Id)/resource/subscriptions/$($CurrentSubscription.SubscriptionId)"}}, `
            @{N="DefenderForVirtualMachines";E={$defenderforcloud["VirtualMachines"]}}, `
            @{N="DefenderForAppServices";E={$defenderforcloud["AppServices"]}}, `
            @{N="DefenderForSqlServers";E={$defenderforcloud["SqlServers"]}}, `
            @{N="DefenderForSqlServerVirtualMachines";E={$defenderforcloud["SqlServerVirtualMachines"]}}, `
            @{N="DefenderForOpenSourceDBs";E={$defenderforcloud["OpenSourceRelationalDatabases"]}}, `
            @{N="DefenderForCosmosDbs";E={$defenderforcloud["CosmosDbs"]}}, `
            @{N="DefenderForStorageAccounts";E={$defenderforcloud["StorageAccounts"]}}, `
            @{N="DefenderForContainerRegistry";E={$defenderforcloud["ContainerRegistry"]}}, `
            @{N="DefenderForKubernetesService";E={$defenderforcloud["KubernetesService"]}}, `
            @{N="DefenderForContainers";E={$defenderforcloud["Containers"]}}, `
            @{N="DefenderForKeyVaults";E={$defenderforcloud["KeyVaults"]}}, `
            @{N="DefenderForDNS";E={$defenderforcloud["DNS"]}}, `
            @{N="DefenderForArm";E={$defenderforcloud["Arm"]}}, `
            @{N="HasVirtualMachines";E={$defenderforcloudres["VirtualMachines"]}}, `
            @{N="HasAppServices";E={$defenderforcloudres["AppServices"]}}, `
            @{N="HasSqlServers";E={$defenderforcloudres["SqlServers"]}}, `
            @{N="HasSqlServerVirtualMachines";E={$defenderforcloudres["SqlServerVirtualMachines"]}}, `
            @{N="HasOpenSourceDBs";E={$defenderforcloudres["OpenSourceRelationalDatabases"]}}, `
            @{N="HasCosmosDbs";E={$defenderforcloudres["CosmosDbs"]}}, `
            @{N="HasStorageAccounts";E={$defenderforcloudres["StorageAccounts"]}}, `
            @{N="HasContainerRegistry";E={$defenderforcloudres["ContainerRegistry"]}}, `
            @{N="HasKubernetesService";E={$defenderforcloudres["KubernetesService"]}}, `
            @{N="HasContainers";E={$defenderforcloudres["Containers"]}}, `
            @{N="HasKeyVaults";E={$defenderforcloudres["KeyVaults"]}}
        $ResourceGroupNames = @($ResourceGroupName)
        if (($PSCmdlet.ParameterSetName -eq "SubnRg" -and !$ResourceGroupName) -or $PSCmdlet.ParameterSetName -eq "MgmtGrp") {
            $ResourceGroupNames = Invoke-RetryCommand { Get-AzResourceGroup } | foreach-object { $_.ResourceGroupName }
        }
        foreach($CurrentResourceGroupName in $ResourceGroupNames) {      
            $resourcegroupinfos += "" | select-object @{N="SubscriptionId";E={$CurrentSubscription.SubscriptionId}},@{N="Id";E={"/subscriptions/$($CurrentSubscription.SubscriptionId)/resourceGroups/$CurrentResourceGroupName"}},@{N="Name";E={$CurrentResourceGroupName}},@{N="Link";E={"https://portal.azure.com/#@$($context.Tenant.Id)/resource/subscriptions/$($CurrentSubscription.SubscriptionId)/resourceGroups/$CurrentResourceGroupName"}}
        }
    }
    # save subscriptions infos
    $subscriptionsinfos | Export-Csv -Path $subscriptionscsv -NoTypeInformation -Append
    # save resourcegroup infos
    $resourcegroupinfos | Export-Csv -Path $resourcegroupscsv -NoTypeInformation -Append
    # use job queries
    $jobs = @()
    $cnt = 0
    foreach($ResourceType in $ResourceTypes) {
        foreach($ResourceGroupInfo in $resourcegroupinfos) {
            $jobs += Invoke-AzRestMethod -AsJob -Method GET -Path "/subscriptions/$($ResourceGroupInfo.SubscriptionId)/resourceGroups/$($ResourceGroupInfo.Name)/resources?`$filter=resourceType+eq+'$($ResourceType)'&`$top=1&api-version=2021-04-01"
            if ($jobs.Count -ge 20) {
                $cnt += $jobs.Count
                $collections += $jobs | wait-job | Receive-Job | Where-Object {($_.StatusCode -eq 200 -and $_.Content -ne "{`"value`":[]}")} | ForEach-Object { ($_.Content | convertfrom-json).Value[0].id  } | Select-Object @{N="SubscriptionId";E={($_ -split "/")[2]}},@{N="ResourceGroupName";E={($_ -split "/")[4]}},@{N="ResourceType";E={($_ -split "/")[6..7] -join "/"}}
                Write-Progress -Activity "checking resources collection" -Status "$cnt of $($ResourceTypes.Count * $ResourceGroupInfos.Count)" -PercentComplete (($cnt / ($ResourceTypes.Count * $ResourceGroupInfos.Count))*100)
                $jobs = @()
            }
        }
    }
    $cnt += $jobs.Count
    $collections += $jobs | wait-job | Receive-Job | Where-Object {($_.StatusCode -eq 200 -and $_.Content -ne "{`"value`":[]}")} | ForEach-Object { ($_.Content | convertfrom-json).Value[0].id  } | Select-Object @{N="SubscriptionId";E={($_ -split "/")[2]}},@{N="ResourceGroupName";E={($_ -split "/")[4]}},@{N="ResourceType";E={($_ -split "/")[6..7] -join "/"}}
    Write-Progress -Activity "checking resources collection" -Status "$cnt of $($ResourceTypes.Count * $ResourceGroupInfos.Count)" -PercentComplete (($cnt / ($ResourceTypes.Count * $ResourceGroupInfos.Count))*100)
    
    # adding role assignement to the collections
    $collections += $resourcegroupinfos | Select-Object SubscriptionId,@{N="ResourceGroupName";E={$_.Name}},@{N="ResourceType";E={"roles"}}

    # sort by subscription id to limit context changes
    $collections = $collections | Sort-Object -Property SubscriptionId

    # Start the collections showing a progress bar
    for($colid = 0; $colid -lt $collections.Count; $colid += 1) {
        $SubscriptionId = $collections[$colid].SubscriptionId
        $ResourceGroupName = $collections[$colid].ResourceGroupName
        $ResourceType = $collections[$colid].ResourceType
        if ($ResourceType -eq "Microsoft.Network/privateEndpoints") {
            # Get all private endpoints job
            Get-AzureAssessResPrivateEndpoints -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName | Export-Csv -Path $privateendpointscsv -NoTypeInformation -Append
        }
        if ($ResourceType -eq "Microsoft.Web/sites") {
            # Get all webapps job
            Get-AzureAssessResWebApps -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName | Export-Csv -Path $webappscsv -NoTypeInformation -Append
        }
        if ($ResourceType -eq "Microsoft.Web/serverFarms") {
            # Get all app service plans job
            Get-AzureAssessResAppServicePlans -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName | Export-Csv -Path $appserviceplanscsv -NoTypeInformation -Append
        }
        if ($ResourceType -eq "Microsoft.Storage/storageAccounts") {
            # Get all storage accounts job
            Get-AzureAssessResStorages -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName | Export-Csv -Path $storagecsv -NoTypeInformation -Append
        }
        if ($ResourceType -eq "Microsoft.Sql/servers") {
            # Get all sql servers job
            Get-AzureAssessResSqlServers -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName | Export-Csv -Path $sqlserverscsv -NoTypeInformation -Append
        }
        if ($ResourceType -eq "Microsoft.KeyVault/vaults") {
            # Get all key vaults and export to csv
            Get-AzureAssessResKeyVaults -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName | Export-Csv -Path $keyvaultscsv -NoTypeInformation -Append
        }
        if ($ResourceType -eq "Microsoft.Compute/virtualMachines") {
            # Get all virualmachines and export to csv
            Get-AzureAssessResVirtualMachines -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName | Export-Csv -Path $virtualmachinescsv -NoTypeInformation -Append
        }
        if ($ResourceType -eq "roles") {
            # Get all role assignements and export to csv
            Get-AzureAssessPrivilegedRoleAssignments -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName | Export-Csv -Path $roleassignmentscsv -NoTypeInformation -Append
        }
        # Get all Defender for cloud recommendations
        Get-AzureAssessResSecurityRecommendations -SubscriptionId $SubscriptionId -ResourceGroupName $ResourceGroupName -ResourceType $ResourceType | Export-Csv -Path $securityrecommendationscsv -NoTypeInformation -Append
        Write-Progress -Activity "Collecting resources" -Status "$colid of $($collections.Count)" -PercentComplete ($colid / $collections.Count * 100)
    }

    # Add information about private endpoints
    Join-AzureAssessPrivateEndpoints

    # remove duplicates from role assignements
    $uniqueroleassignments = Get-Content $roleassignmentscsv | ConvertFrom-Csv | Sort-Object | Get-Unique -AsString -CaseInsensitive
    $uniqueroleassignments | Export-Csv -Path $roleassignmentscsv -NoTypeInformation -Force
}

# SIG # Begin signature block
# MIIRWAYJKoZIhvcNAQcCoIIRSTCCEUUCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAfwOXqdDa2FpeS
# 0Y7YzEJnlkIrdyaoUHb5v403pVw346CCDZIwgga5MIIEoaADAgECAhEAmaOACiZV
# O2Wr3G6EprPqOTANBgkqhkiG9w0BAQwFADCBgDELMAkGA1UEBhMCUEwxIjAgBgNV
# BAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1bSBD
# ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQg
# TmV0d29yayBDQSAyMB4XDTIxMDUxOTA1MzIxOFoXDTM2MDUxODA1MzIxOFowVjEL
# MAkGA1UEBhMCUEwxITAfBgNVBAoTGEFzc2VjbyBEYXRhIFN5c3RlbXMgUy5BLjEk
# MCIGA1UEAxMbQ2VydHVtIENvZGUgU2lnbmluZyAyMDIxIENBMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAnSPPBDAjO8FGLOczcz5jXXp1ur5cTbq96y34
# vuTmflN4mSAfgLKTvggv24/rWiVGzGxT9YEASVMw1Aj8ewTS4IndU8s7VS5+djSo
# McbvIKck6+hI1shsylP4JyLvmxwLHtSworV9wmjhNd627h27a8RdrT1PH9ud0IF+
# njvMk2xqbNTIPsnWtw3E7DmDoUmDQiYi/ucJ42fcHqBkbbxYDB7SYOouu9Tj1yHI
# ohzuC8KNqfcYf7Z4/iZgkBJ+UFNDcc6zokZ2uJIxWgPWXMEmhu1gMXgv8aGUsRda
# CtVD2bSlbfsq7BiqljjaCun+RJgTgFRCtsuAEw0pG9+FA+yQN9n/kZtMLK+Wo837
# Q4QOZgYqVWQ4x6cM7/G0yswg1ElLlJj6NYKLw9EcBXE7TF3HybZtYvj9lDV2nT8m
# FSkcSkAExzd4prHwYjUXTeZIlVXqj+eaYqoMTpMrfh5MCAOIG5knN4Q/JHuurfTI
# 5XDYO962WZayx7ACFf5ydJpoEowSP07YaBiQ8nXpDkNrUA9g7qf/rCkKbWpQ5bou
# fUnq1UiYPIAHlezf4muJqxqIns/kqld6JVX8cixbd6PzkDpwZo4SlADaCi2JSplK
# ShBSND36E/ENVv8urPS0yOnpG4tIoBGxVCARPCg1BnyMJ4rBJAcOSnAWd18Jx5n8
# 58JSqPECAwEAAaOCAVUwggFRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFN10
# XUwA23ufoHTKsW73PMAywHDNMB8GA1UdIwQYMBaAFLahVDkCw6A/joq8+tT4HKbR
# Og79MA4GA1UdDwEB/wQEAwIBBjATBgNVHSUEDDAKBggrBgEFBQcDAzAwBgNVHR8E
# KTAnMCWgI6Ahhh9odHRwOi8vY3JsLmNlcnR1bS5wbC9jdG5jYTIuY3JsMGwGCCsG
# AQUFBwEBBGAwXjAoBggrBgEFBQcwAYYcaHR0cDovL3N1YmNhLm9jc3AtY2VydHVt
# LmNvbTAyBggrBgEFBQcwAoYmaHR0cDovL3JlcG9zaXRvcnkuY2VydHVtLnBsL2N0
# bmNhMi5jZXIwOQYDVR0gBDIwMDAuBgRVHSAAMCYwJAYIKwYBBQUHAgEWGGh0dHA6
# Ly93d3cuY2VydHVtLnBsL0NQUzANBgkqhkiG9w0BAQwFAAOCAgEAdYhYD+WPUCia
# U58Q7EP89DttyZqGYn2XRDhJkL6P+/T0IPZyxfxiXumYlARMgwRzLRUStJl490L9
# 4C9LGF3vjzzH8Jq3iR74BRlkO18J3zIdmCKQa5LyZ48IfICJTZVJeChDUyuQy6rG
# DxLUUAsO0eqeLNhLVsgw6/zOfImNlARKn1FP7o0fTbj8ipNGxHBIutiRsWrhWM2f
# 8pXdd3x2mbJCKKtl2s42g9KUJHEIiLni9ByoqIUul4GblLQigO0ugh7bWRLDm0Cd
# Y9rNLqyA3ahe8WlxVWkxyrQLjH8ItI17RdySaYayX3PhRSC4Am1/7mATwZWwSD+B
# 7eMcZNhpn8zJ+6MTyE6YoEBSRVrs0zFFIHUR08Wk0ikSf+lIe5Iv6RY3/bFAEloM
# U+vUBfSouCReZwSLo8WdrDlPXtR0gicDnytO7eZ5827NS2x7gCBibESYkOh1/w1t
# VxTpV2Na3PR7nxYVlPu1JPoRZCbH86gc96UTvuWiOruWmyOEMLOGGniR+x+zPF/2
# DaGgK2W1eEJfo2qyrBNPvF7wuAyQfiFXLwvWHamoYtPZo0LHuH8X3n9C+xN4YaNj
# t2ywzOr+tKyEVAotnyU9vyEVOaIYMk3IeBrmFnn0gbKeTTyYeEEUz/Qwt4HOUBCr
# W602NCmvO1nm+/80nLy5r0AZvCQxaQ4wggbRMIIEuaADAgECAhBNn7NsFkgeJAAl
# SKxTsSjWMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhB
# c3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBDb2RlIFNp
# Z25pbmcgMjAyMSBDQTAeFw0yNDAxMTYxMjAwMzRaFw0yNTAxMTUxMjAwMzNaMHcx
# CzAJBgNVBAYTAkJFMRgwFgYDVQQIDA9XYWxsb29uIEJyYWJhbnQxHjAcBgNVBAoM
# FU9wZW4gU291cmNlIERldmVsb3BlcjEuMCwGA1UEAwwlT3BlbiBTb3VyY2UgRGV2
# ZWxvcGVyLCBDZWRyaWMgQmxvbWFydDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
# AgoCggIBALp1WzPLBGmSjvJ2se9K+HWIzxlPIh9vmddaj0Sc6Yz3jJizVN7WphR7
# 1kR4KFEfCFL6eleZlIkqDMkNfwBTpfVCx7MYR85YSceHo8ieEIV5MmWSZ1CR36/A
# wXrUvXzlX85Rf/RuotfluSmdhTRoBbCN9aIsSm50pEZixvUOcipTNZbuY29NvjXP
# JAH1RX0od6QYCGt/v+5C+hBippk+QohxmKQYLilh4+58fdZvnXaKPvEuEpbhXCeh
# /HbASrvTbZXXbh8fMbHOq+xnCemK/6aqjfqgRzkXn9unjl6wj6KT7MoEp1tCn+9z
# cmi/zW3KeI3Alld5B6IEvVfZVmaCrvSsRBNFnVpVg5eGQNRyan0xfblEwKLrsoNk
# WhUtJY3SD53kvz8OHBKRY1quaq70UefcIDuPq4+76CVovjbpT/wnmP6PFnHtNrDj
# KzMAnUWOeseIcDhp7ETzVyoh2rWofTpXUr5aEau5mkXs0MclqjimMeWf0r3NyBa5
# E3k0rG/xtpmTFB9e4w7b0VQUxNUcVdmocGL7Rj4ouWLjUDUkiE2DctxMTaPmqKJE
# nx8cFsBCrDmgNYxGOZ03ykPmPbwc3SooZeS5rRmR4v2GTVrbsmbiiggtYDjhJddo
# sC/TtJ+65JL48oTHDK1KVMtSjUBr/Q16Nzg0JaXfiZ4JS+MX45ylAgMBAAGjggF4
# MIIBdDAMBgNVHRMBAf8EAjAAMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jY3Nj
# YTIwMjEuY3JsLmNlcnR1bS5wbC9jY3NjYTIwMjEuY3JsMHMGCCsGAQUFBwEBBGcw
# ZTAsBggrBgEFBQcwAYYgaHR0cDovL2Njc2NhMjAyMS5vY3NwLWNlcnR1bS5jb20w
# NQYIKwYBBQUHMAKGKWh0dHA6Ly9yZXBvc2l0b3J5LmNlcnR1bS5wbC9jY3NjYTIw
# MjEuY2VyMB8GA1UdIwQYMBaAFN10XUwA23ufoHTKsW73PMAywHDNMB0GA1UdDgQW
# BBQ2dBi+EnTsl++Xmrax5h7dyzC12TBLBgNVHSAERDBCMAgGBmeBDAEEATA2Bgsq
# hGgBhvZ3AgUBBDAnMCUGCCsGAQUFBwIBFhlodHRwczovL3d3dy5jZXJ0dW0ucGwv
# Q1BTMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG
# 9w0BAQsFAAOCAgEAZm3wL05QNFh25ll601jtST553L2VVS4ugvlHmX90PSOkjmX3
# jQ2ERwdz9MHWhoOvW1EdOrBlm31YRt2GmCz4/82vq9fTTNwLSzk3csHEIgHuAbN1
# INCVgz0l7Ya7mkxFLHoZ4BJ9LluS4p2NjvU9r7OGnBCgfFaLriC+qRY8QxCeMQOl
# l1BTTaUBMp0pgxh3XOOxOQfNeFCkXg0q4QULkaRuApJFZi/pY+PMA6p8bjOTP4YI
# 9VNsoc7ReBYMDL588oupc1CTiDaf0e6YaN80z6WoIGuNja3VPDEG4VmWZG+Of9gO
# XUNllvR8n1IXXsvWEuHRTCV9jjK89NHGaNroeXHr1C6eERnoNUPbEuIb/parUnGB
# n5MKWL+2TlL9Z34lSFz6e1Efi4oJeDznUojNZKJzOMvBS+JJyr0aVuW4lDoqbqkl
# 9vFLjHi1oGM7mBrlL8AHEC3iUNEOwgPcbsIYAqV85RyXYNQqLu1ik1MmmpKy/Tma
# Mmi/AiXxtjC6RrnrkN6tIHR7Nky9b5jWfMxCCG2b119gr7SkJ8qVOH8I9oBxMDPR
# IjBi5rEjSbDOCpRhZiqmx7K7gW2Gyp1OCIffVyiqmeZh+YLxpdP0tymxtqg/HVqV
# If3xn0Sgl7HcUpcRkY3EgTPUqNaVzOSO5h8KyzA9nVtUX2XwSx9C4vb6uqcxggMc
# MIIDGAIBATBqMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBT
# eXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBDb2RlIFNpZ25pbmcgMjAyMSBD
# QQIQTZ+zbBZIHiQAJUisU7Eo1jANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3
# AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisG
# AQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCC5yQ+VJJ7j
# 0tBYYVKSqYLfUM4hQsMeAaI2hShopDUpfzANBgkqhkiG9w0BAQEFAASCAgAGry8Y
# TU335LkDYwj1g2PfEuAbf8/FcL8OK8dXjAYfEvEZBvj/DM9iYRRuX9kok3VZdoEM
# nXzrZaYcZJNcjeDGSa65tp4QteC5wu6VH3mjc0ZqkUplxyzIZ7Wn+ZynrMJK9h30
# ZpC7vtOtQ93TARYSGLevqCFFwQX6WWqIqwJ+dAtpzBrNta4IRbiQXYb0gEWLAQQ2
# gsKyyU5btpTdrnbVDjcaj2mb8IdqTCJIW656PuZxLxjb9u5Sqq4hX+O3CPlm+3kA
# 9FPyr7LEseDcOkFXzUSluDIzM1FJHFexQgBTvF8gfjhEzXDzGA3x5umFYyU+A3IG
# DYKgSGKDYXdgDY4TesfBddLTWAE3P2Bgio1fePZmsHHjdacwyt/3U1sUeBNAYcmZ
# GBI/oXVH/ZDpH5IAMK9+PtYPHqaBhZ/xFHusANBERS7VDCE4O2rKQ2xySdHkqTGa
# l6k/lI5WGrh4b7coV9xt2E7/qmdkR3eQU+o2IErI0h0siNf5XiVATcR3riBcO2dO
# K8jxT0ZO874sb6VRtPoXsuQxa32Wqzm4MQfApL+LW2WkcTfqkZMxsL/G9Z0MB81Z
# /Bxcbx1ZV/Lg7lg4GNmss3CPD1UcZ4VbIn8JJlY4Q+qFV8fNPAw6qvNuZWnG7gVR
# BpGHmneFn2oK8Aeykkp02p8RJPLMxxl2ytGEaQ==
# SIG # End signature block