Framework/Core/SVT/Services/SQLDatabase.ps1

using namespace Microsoft.Azure.Commands.Sql.Auditing.Model
using namespace Microsoft.Azure.Commands.Sql.ServerUpgrade.Model
using namespace Microsoft.Azure.Commands.Sql.TransparentDataEncryption.Model
using namespace Microsoft.Azure.Commands.Sql.ThreatDetection.Model

Set-StrictMode -Version Latest 
class SQLDatabase: SVTBase
{       
    hidden [PSObject] $ResourceObject;

    SQLDatabase([string] $subscriptionId, [string] $resourceGroupName, [string] $resourceName): 
        Base($subscriptionId, $resourceGroupName, $resourceName) 
    { 
        $this.GetResourceObject();
    }
    
    SQLDatabase([string] $subscriptionId, [SVTResource] $svtResource): 
        Base($subscriptionId, $svtResource) 
    { 
        $this.GetResourceObject();
    }

    hidden [PSObject] GetResourceObject()
    {
        if (-not $this.ResourceObject) {
            $this.ResourceObject =   Get-AzureRmResource -ResourceName $this.ResourceContext.ResourceName -ResourceGroupName $this.ResourceContext.ResourceGroupName 
                                                         
            if(-not $this.ResourceObject)
            {
                throw ("Resource '{0}' not found under Resource Group '{1}'" -f ($this.ResourceContext.ResourceName), ($this.ResourceContext.ResourceGroupName))
            }
        }
        return $this.ResourceObject;
    }

    hidden [ControlResult] CheckSqlServerVersionUpgrade([ControlResult] $controlResult)
    {
        $upgradeStatus = Get-AzureRmSqlServerUpgrade -ResourceGroupName  $this.ResourceContext.ResourceGroupName -ServerName $this.ResourceContext.ResourceName -ErrorAction Stop

        $controlResult.AddMessage([MessageData]::new("Current status of SQL Database server upgrade -",
                                                     $upgradeStatus));

        if ($upgradeStatus.Status -eq [ServerUpgradeStatus]::Completed)   
        {
            $controlResult.VerificationResult = [VerificationResult]::Passed
        }                 
        else
        {
            $controlResult.VerificationResult = [VerificationResult]::Failed
        }

        return $controlResult;
    }

    
    hidden [ControlResult] CheckSqlServerAuditing([ControlResult] $controlResult)
    {
        $serverAudit = Get-AzureRmSqlServerAuditingPolicy -ResourceGroupName $this.ResourceContext.ResourceGroupName -ServerName $this.ResourceContext.ResourceName -ErrorAction Stop
       
        $controlResult.AddMessage([MessageData]::new("Current audit status for SQL server [$($this.ResourceContext.ResourceName)]:", $serverAudit))
        
        if($null -ne $serverAudit){ 
            #Check for Audit type as Blob (Table audit type going to be deprecated)
            if($serverAudit.AuditType -eq [AuditType]::Blob){
                
                $isCompliant = (($serverAudit.AuditState -eq [AuditStateType]::Enabled) `
                               -and ($serverAudit.RetentionInDays -eq $this.ControlSettings.SqlServer.AuditRetentionPeriod_Min -or $serverAudit.RetentionInDays -eq $this.ControlSettings.SqlServer.AuditRetentionPeriod_Forever))
                
                if ($isCompliant){
                           $controlResult.VerificationResult = [VerificationResult]::Passed
                    }                 
                    else{
                        $controlResult.EnableFixControl = $true;
                        $controlResult.AddMessage([VerificationResult]::Failed, 
                                                  "Audit settings are either disabled OR not retaining logs for at least $($this.ControlSettings.SqlServer.AuditRetentionPeriod_Min) days for resource - [$($this.ResourceContext.ResourceName)]");
                    }
            }
            else{
                $controlResult.EnableFixControl = $true;
                $controlResult.AddMessage([VerificationResult]::Failed, "SQL server auditing is not enabled with 'Audit Type' as 'Blob' for SQL server ["+ $this.ResourceContext.ResourceName +"]");
            }

        }
        else{
            $controlResult.AddMessage("Unable to get audit details for SQL server [$($this.ResourceContext.ResourceName)]"); 
        }
   
        return $controlResult;
    }

   hidden [ControlResult[]] CheckSqlDatabaseAuditing([ControlResult] $controlResult)
   {
        [ControlResult[]] $resultControlResultList = @()

        $sqlDataBaseDtls = Get-AzureRmSqlDatabase -ResourceGroupName $this.ResourceContext.ResourceGroupName -ServerName $this.ResourceContext.ResourceName -ErrorAction Stop | 
            Where-Object { $_.DatabaseName -ne "master" } 

        if(($sqlDataBaseDtls | Measure-Object ).Count -eq 0)
        {
            $controlResult.AddMessage([MessageData]::new("No database found on SQL Server ["+ $this.ResourceContext.ResourceName +"]"));
            #Passing the status as there is no database found on the SQL Server
            $controlResult.VerificationResult = [VerificationResult]::Passed; 
            $resultControlResultList += $controlResult
        }
        else
        {
            $sqlDataBaseDtls | 
            ForEach-Object {
                $dbName = $_.DatabaseName;
                #[ControlResult] $childControlResult = $this.CreateControlResult($dbName);
                [ControlResult] $childControlResult = $this.CreateChildControlResult($dbName, $controlResult);
                        
                $dbAuditPolicy = Get-AzureRmSqlDatabaseAuditingPolicy `
                                    -ResourceGroupName $this.ResourceContext.ResourceGroupName `
                                    -ServerName $this.ResourceContext.ResourceName `
                                    -DatabaseName $dbName `
                                    -ErrorAction Stop
                        
                if($null -ne $dbAuditPolicy){ 
   
                     $childControlResult.AddMessage([MessageData]::new("Current audit details for database [$($dbName)]:", $dbAuditPolicy));

                     #Check for Audit type as Blob (Table audit type going to be deprecated)
                     if($dbAuditPolicy.AuditType -eq [AuditType]::Blob){
                
                                    $isCompliant = (($dbAuditPolicy.AuditState -eq [AuditStateType]::Enabled) `
                                                   -and ($dbAuditPolicy.RetentionInDays -eq $this.ControlSettings.SqlServer.AuditRetentionPeriod_Min -or $dbAuditPolicy.RetentionInDays -eq $this.ControlSettings.SqlServer.AuditRetentionPeriod_Forever))
                
                                    if($isCompliant){
                                               $childControlResult.VerificationResult = [VerificationResult]::Passed
                                        }                 
                                        else{
                                            $childControlResult.EnableFixControl = $true;
                                            $childControlResult.AddMessage([VerificationResult]::Failed, 
                                                                           "Audit settings are either disabled OR not retaining logs for at least $($this.ControlSettings.SqlServer.AuditRetentionPeriod_Min) days for database - [$($dbName)]");
                                        }
                                }
                                else{
                                    $childControlResult.EnableFixControl = $true;

                                    $childControlResult.AddMessage([VerificationResult]::Failed, "SQL database auditing is not enabled with 'Audit Type' as 'Blob' for SQL database - [$($dbName)]");
                                }
                    }
                    else{
                         $childControlResult.AddMessage("Unable to get database audit details for [$($dbName)]"); 
                    }
                $resultControlResultList += $childControlResult
            }
        }
         
        return $resultControlResultList; 
    }
       
    hidden [ControlResult[]] CheckSqlDatabaseTDE([ControlResult] $controlResult)
    {
        [ControlResult[]] $resultControlResultList = @()

        $sqlDataBaseDtls = Get-AzureRmSqlDatabase -ResourceGroupName $this.ResourceContext.ResourceGroupName -ServerName $this.ResourceContext.ResourceName -ErrorAction Stop | 
            Where-Object { $_.DatabaseName -ne "master" } 
               
        if(($sqlDataBaseDtls | Measure-Object ).Count -eq 0)
        {
            $controlResult.AddMessage([MessageData]::new("No database found on SQL Server - ["+ $this.ResourceContext.ResourceName +"]"));
            #Since there is no database found we are passing this control
            $controlResult.VerificationResult = [VerificationResult]::Passed; 
            $resultControlResultList += $controlResult
        }
        else
        {
            $sqlDataBaseDtls | ForEach-Object {
                $dbName = $_.DatabaseName;
                [ControlResult] $childControlResult = $this.CreateChildControlResult($dbName, $controlResult);

                $tdeStatus = Get-AzureRmSqlDatabaseTransparentDataEncryption `
                                -ResourceGroupName $this.ResourceContext.ResourceGroupName `
                                -ServerName $this.ResourceContext.ResourceName `
                                -DatabaseName $dbName `
                                -ErrorAction Stop

                $childControlResult.AddMessage([MessageData]::new("Current TDE Status for - ["+ $dbName +"]", 
                                                ($tdeStatus )));      
                        
                if($tdeStatus.State -eq [TransparentDataEncryptionStateType]::Enabled){
                        $childControlResult.VerificationResult = [VerificationResult]::Passed; 
                }
                else
                {
                    $childControlResult.VerificationResult = [VerificationResult]::Failed;
                    $childControlResult.EnableFixControl = $true;

                    #if($childControlResult.FixControlParameters)
                    #{
                    # $childControlResult.FixControlParameters.DatabaseName = $dbName;
                    #}
                }
                $resultControlResultList += $childControlResult
            } #End of ForEach-Object
        }
        return  $resultControlResultList;
    }

      
       hidden [ControlResult] CheckSqlServerADAdmin([ControlResult] $controlResult)
        {
            $adAdmin = Get-AzureRmSqlServerActiveDirectoryAdministrator -ResourceGroup $this.ResourceContext.ResourceGroupName -Server $this.ResourceContext.ResourceName -ErrorAction Stop
              
            $controlResult.AddMessage([MessageData]::new("Current status of Active Directory Admin for ["+ $this.ResourceContext.ResourceName +"] is"));

            if(($adAdmin | Measure-Object).Count -gt 0){
                 $controlResult.VerificationResult = [VerificationResult]::Passed
                 $controlResult.AddMessage([MessageData]::new("Active Directory admins are assigned on SQL Server - ["+ $this.ResourceContext.ResourceName +"]", 
                                                             ($adAdmin)));
            }
            else{
                $controlResult.VerificationResult = [VerificationResult]::Failed
                $controlResult.AddMessage([MessageData]::new("No Active Directory admin is assigned on SQL Server - ["+ $this.ResourceContext.ResourceName +"]"));
                $controlResult.EnableFixControl = $true;
            }
            return $controlResult
        }

       
        hidden [ControlResult] CheckSqlServerThreatDetection([ControlResult] $controlResult)
        { 
            $isCompliant = $false
            $serverThreat = Get-AzureRmSqlServerThreatDetectionPolicy `
                                -ResourceGroupName $this.ResourceContext.ResourceGroupName `
                                -ServerName $this.ResourceContext.ResourceName `
                                -ErrorAction Stop

            $controlResult.AddMessage([MessageData]::new("Current threat detection status for server ["+ $this.ResourceContext.ResourceName +"] is",
                                                        ($serverThreat)));

            $excludedTypeCount = ($serverThreat.ExcludedDetectionTypes | Measure-Object ).Count

            if($excludedTypeCount -gt 0){
                $controlResult.AddMessage([MessageData]::new("All the required audit event types are not enabled for SQL Server - ["+ $this.ResourceContext.ResourceName +"]"));
            }

            $isCompliant =  (($serverThreat.ThreatDetectionState -eq [ThreatDetectionStateType]::Enabled) `
                                -and ($excludedTypeCount -eq 0) `
                                -and (($serverThreat.EmailAdmins  -eq $True) -or ($null -ne $serverThreat.NotificationRecipientsEmails)))
            if ($isCompliant) {
                $controlResult.VerificationResult = [VerificationResult]::Passed
            }                 
            else{
                $controlResult.EnableFixControl = $true;
                $controlResult.VerificationResult = [VerificationResult]::Failed
            }
             
          return $controlResult
        }

        hidden [ControlResult[]] CheckSqlDatabaseThreatDetection([ControlResult] $controlResult)
        {
            [ControlResult[]] $resultControlResultList = @()

            $sqlDataBaseDtls = Get-AzureRmSqlDatabase -ResourceGroupName $this.ResourceContext.ResourceGroupName -ServerName $this.ResourceContext.ResourceName -ErrorAction Stop | 
                Where-Object { $_.DatabaseName -ne "master" } 
               
            if(($sqlDataBaseDtls | Measure-Object ).Count -eq 0)
            {
                $controlResult.AddMessage([MessageData]::new("No database found on SQL Server - ["+ $this.ResourceContext.ResourceName +"]"));
                #Since there is no database found we are passing this control
                $controlResult.VerificationResult = [VerificationResult]::Passed; 
                $resultControlResultList += $controlResult
            }
            else
            {
                $sqlDataBaseDtls | ForEach-Object {
                    $dbName = $_.DatabaseName;
                    [ControlResult] $childControlResult = $this.CreateChildControlResult($dbName, $controlResult);
 
                    #$telemetryTimerPerDb = [System.Diagnostics.Stopwatch]::StartNew()
                    $dbThreat = Get-AzureRmSqlDatabaseThreatDetectionPolicy `
                                    -ResourceGroupName $this.ResourceContext.ResourceGroupName `
                                    -ServerName $this.ResourceContext.ResourceName `
                                    -DatabaseName $dbName `
                                    -ErrorAction Stop

                    $childControlResult.AddMessage([MessageData]::new("Current threat detection state for database - ["+ $dbName +"]", 
                                                                      ($dbThreat)));      
                            
     # $dbAuditPolicy = Get-AzureRmSqlDatabaseAuditingPolicy `
     # -ResourceGroupName $this.ResourceContext.ResourceGroupName `
     # -ServerName $this.ResourceContext.ResourceName `
     # -DatabaseName $dbName `
     # -ErrorAction Stop

     # $childControlResult.AddMessage([MessageData]::new("Current audit status for database - ["+ $dbName +"]",
     # ($dbAuditPolicy )));
                    
     # if(Get-Member -InputObject $dbAuditPolicy -Name 'UseServerDefault' -MemberType Properties)
                    #{
                    # $isCompliant = $dbAuditPolicy.UseServerDefault -eq [UseServerDefaultOptions]::Enabled
                    #}
                    #else
                    #{
                    # $isCompliant = $false
                    #}


                    #if(-not $isCompliant) {
                        if(($dbThreat.ThreatDetectionState -eq [ThreatDetectionStateType]::Enabled) `
                            -and ($dbThreat.ExcludedDetectionTypes.Count -eq 0) `
                            -and (($dbThreat.EmailAdmins -eq $True) -or ($dbThreat.NotificationRecipientsEmails.Length -gt 0)))
                        {
                            $isCompliant = $True
                        }
                        else{
                            $isCompliant = $false                
                        }
                    #}

                if($isCompliant) {
                     $childControlResult.VerificationResult = [VerificationResult]::Passed; 
                }
                else {
                    $childControlResult.EnableFixControl = $true;
                     $childControlResult.VerificationResult = [VerificationResult]::Failed;
                }
               
                 $resultControlResultList += $childControlResult
               }
            }
           
            return  $resultControlResultList;
        }
    
       hidden [ControlResult] CheckSqlDatabaseFirewallEnabled([ControlResult] $controlResult)
        {
          $firewallDtls = Get-AzureRmSqlServerFirewallRule -ResourceGroupName $this.ResourceContext.ResourceGroupName  -ServerName $this.ResourceContext.ResourceName
          if(($firewallDtls | Measure-Object ).Count -gt 0){
               $controlResult.VerificationResult = [VerificationResult]::Passed
            }
            else{
               $controlResult.VerificationResult = [VerificationResult]::Failed 
            }
          return $controlResult
        }

        hidden [ControlResult] CheckSqlDatabaseFirewallIPAddressRange([ControlResult] $controlResult)
        {
          #As current function will check firewall ip address ranges, if firewall is enabled. When it enabled, it allows any traffic from services within your Azure subscription to pass through.
          #Default record will be their with Start IP address as 0.0.0.0 and End Ip address as 0.0.0.0
            $firewallDtls = Get-AzureRmSqlServerFirewallRule -ResourceGroupName $this.ResourceContext.ResourceGroupName  -ServerName $this.ResourceContext.ResourceName
            if(($firewallDtls | Measure-Object ).Count -gt 0)
            {
                $controlResult.AddMessage([MessageData]::new("Current firewall settings for - ["+ $this.ResourceContext.ResourceName +"]", 
                                                             $firewallDtls));

                $anyToAnyRule =  $firewallDtls | Where-Object { $_.StartIpAddress -eq $this.ControlSettings.IPRangeStartIP -and $_.EndIpAddress -eq  $this.ControlSettings.IPRangeEndIP}
                if (($anyToAnyRule | Measure-Object).Count -gt 0)
                {
                    $controlResult.AddMessage([VerificationResult]::Failed, 
                                              [MessageData]::new("Firewall rule covering all IPs (Start IP address: $($this.ControlSettings.IPRangeStartIP) To End IP Address: $($this.ControlSettings.IPRangeEndIP)) is defined."));
                }
                else 
                {
                    $controlResult.VerificationResult = [VerificationResult]::Verify
                }
                $controlResult.SetStateData("Firewall IP addresses", $firewallDtls);
            }
            else
            {
                $controlResult.AddMessage([VerificationResult]::Failed, "Control can not be validated: Firewall is not enabled");
            }
            return $controlResult
        }
}
# SIG # Begin signature block
# MIIkAQYJKoZIhvcNAQcCoIIj8jCCI+4CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAI8KTZJoRauwvh
# 1g+/Pmj9YDZh9GDfcqxWFTp5ICPhbqCCDZMwggYRMIID+aADAgECAhMzAAAAjoeR
# pFcaX8o+AAAAAACOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMTYxMTE3MjIwOTIxWhcNMTgwMjE3MjIwOTIxWjCBgzEL
# MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v
# bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjENMAsGA1UECxMETU9Q
# UjEeMBwGA1UEAxMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMIIBIjANBgkqhkiG9w0B
# AQEFAAOCAQ8AMIIBCgKCAQEA0IfUQit+ndnGetSiw+MVktJTnZUXyVI2+lS/qxCv
# 6cnnzCZTw8Jzv23WAOUA3OlqZzQw9hYXtAGllXyLuaQs5os7efYjDHmP81LfQAEc
# wsYDnetZz3Pp2HE5m/DOJVkt0slbCu9+1jIOXXQSBOyeBFOmawJn+E1Zi3fgKyHg
# 78CkRRLPA3sDxjnD1CLcVVx3Qv+csuVVZ2i6LXZqf2ZTR9VHCsw43o17lxl9gtAm
# +KWO5aHwXmQQ5PnrJ8by4AjQDfJnwNjyL/uJ2hX5rg8+AJcH0Qs+cNR3q3J4QZgH
# uBfMorFf7L3zUGej15Tw0otVj1OmlZPmsmbPyTdo5GPHzwIDAQABo4IBgDCCAXww
# HwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0OBBYEFKvI1u2y
# FdKqjvHM7Ww490VK0Iq7MFIGA1UdEQRLMEmkRzBFMQ0wCwYDVQQLEwRNT1BSMTQw
# MgYDVQQFEysyMzAwMTIrYjA1MGM2ZTctNzY0MS00NDFmLWJjNGEtNDM0ODFlNDE1
# ZDA4MB8GA1UdIwQYMBaAFEhuZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEsw
# SaBHoEWGQ2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0Nv
# ZFNpZ1BDQTIwMTFfMjAxMS0wNy0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsG
# AQUFBzAChkVodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01p
# Y0NvZFNpZ1BDQTIwMTFfMjAxMS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkq
# hkiG9w0BAQsFAAOCAgEARIkCrGlT88S2u9SMYFPnymyoSWlmvqWaQZk62J3SVwJR
# avq/m5bbpiZ9CVbo3O0ldXqlR1KoHksWU/PuD5rDBJUpwYKEpFYx/KCKkZW1v1rO
# qQEfZEah5srx13R7v5IIUV58MwJeUTub5dguXwJMCZwaQ9px7eTZ56LadCwXreUM
# tRj1VAnUvhxzzSB7pPrI29jbOq76kMWjvZVlrkYtVylY1pLwbNpj8Y8zon44dl7d
# 8zXtrJo7YoHQThl8SHywC484zC281TllqZXBA+KSybmr0lcKqtxSCy5WJ6PimJdX
# jrypWW4kko6C4glzgtk1g8yff9EEjoi44pqDWLDUmuYx+pRHjn2m4k5589jTajMW
# UHDxQruYCen/zJVVWwi/klKoCMTx6PH/QNf5mjad/bqQhdJVPlCtRh/vJQy4njpI
# BGPveJiiXQMNAtjcIKvmVrXe7xZmw9dVgh5PgnjJnlQaEGC3F6tAE5GusBnBmjOd
# 7jJyzWXMT0aYLQ9RYB58+/7b6Ad5B/ehMzj+CZrbj3u2Or2FhrjMvH0BMLd7Hald
# G73MTRf3bkcz1UDfasouUbi1uc/DBNM75ePpEIzrp7repC4zaikvFErqHsEiODUF
# he/CBAANa8HYlhRIFa9+UrC4YMRStUqCt4UqAEkqJoMnWkHevdVmSbwLnHhwCbww
# ggd6MIIFYqADAgECAgphDpDSAAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYD
# VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3Nv
# ZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5
# MDlaFw0yNjA3MDgyMTA5MDlaMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIw
# MTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQ
# TTS68rZYIZ9CGypr6VpQqrgGOBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULT
# iQ15ZId+lGAkbK+eSZzpaF7S35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYS
# L+erCFDPs0S3XdjELgN1q2jzy23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494H
# DdVceaVJKecNvqATd76UPe/74ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZ
# PrGMXeiJT4Qa8qEvWeSQOy2uM1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5
# bmR/U7qcD60ZI4TL9LoDho33X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGS
# rhwjp6lm7GEfauEoSZ1fiOIlXdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADh
# vKwCgl/bwBWzvRvUVUvnOaEP6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON
# 7E1JMKerjt/sW5+v/N2wZuLBl4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xc
# v3coKPHtbcMojyyPQDdPweGFRInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqw
# iBfenk70lrC8RqBsmNLg1oiMCwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMC
# AQAwHQYDVR0OBBYEFEhuZOVQBdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQM
# HgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1Ud
# IwQYMBaAFHItOgIxkEO5FAVO4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0
# dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0Nl
# ckF1dDIwMTFfMjAxMV8wM18yMi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUF
# BzAChkJodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0Nl
# ckF1dDIwMTFfMjAxMV8wM18yMi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGC
# Ny4DMIGDMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp
# b3BzL2RvY3MvcHJpbWFyeWNwcy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcA
# YQBsAF8AcABvAGwAaQBjAHkAXwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZI
# hvcNAQELBQADggIBAGfyhqWY4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4s
# PvjDctFtg/6+P+gKyju/R6mj82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKL
# UtCw/WvjPgcuKZvmPRul1LUdd5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7
# pKkFDJvtaPpoLpWgKj8qa1hJYx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft
# 0N3zDq+ZKJeYTQ49C/IIidYfwzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4
# MnEnGn+x9Cf43iw6IGmYslmJaG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxv
# FX1Fp3blQCplo8NdUmKGwx1jNpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG
# 0QaxdR8UvmFhtfDcxhsEvt9Bxw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf
# 0AApxbGbpT9Fdx41xtKiop96eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkY
# S//WsyNodeav+vyL6wuA6mk7r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrv
# QQqxP/uozKRdwaGIm1dxVk5IRcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIV
# xDCCFcACAQEwgZUwfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAA
# AI6HkaRXGl/KPgAAAAAAjjANBglghkgBZQMEAgEFAKCBsDAZBgkqhkiG9w0BCQMx
# DAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkq
# hkiG9w0BCQQxIgQg0Fane0mkooMB8fM4tFy6bNtMFJsHL5DyUIe3kNaIb/EwRAYK
# KwYBBAGCNwIBDDE2MDSgEoAQAEEAegBTAEQASwAyADUAMqEegBxodHRwczovL2Fr
# YS5tcy9henNka29zc2RvY3MgMA0GCSqGSIb3DQEBAQUABIIBAEiK6iVVr9cEX/oT
# YE8LihoM7yTHY+N+GVKiTJ5JUXGI6kXG4nH6e87EJvyG1dzMNXMxlELqaTlR43Uy
# /Zcc+Wh4/kHlgGKQWUT3WFc+IKl7eJakxtjPnusKZFUXEg1evfHOgZ82BSyEdCDw
# 3mvjbBd6CEnaYtK/CLb/kj+rU8W5paFVOPNWKwqbbGOA+5t2Nt5P0HLpW8WLqcJ/
# F97BuU4+BDoLT0xlFs6Jehvg1aeoOgs+g87lt7HRcGU2JSi0CkcKev883JybX7YY
# WtfEliol8VRjbvpthWsmKSblLl/wa9L3arKn1EVOm8LFp7Ma7uLo25TfbldtKnAo
# 8I4Za/6hghNMMIITSAYKKwYBBAGCNwMDATGCEzgwghM0BgkqhkiG9w0BBwKgghMl
# MIITIQIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBPQYLKoZIhvcNAQkQAQSgggEsBIIB
# KDCCASQCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQgN3XFpstRxwLY
# aPG4AnfxR8QpPRaHX1vtqej88MTcyG4CBlmSFyJyiRgTMjAxNzA5MDUwOTM3MTUu
# ODAzWjAHAgEBgAIB9KCBuaSBtjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
# b3Jwb3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0Ug
# RVNOOkIxQjctRjY3Ri1GRUMyMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt
# cCBTZXJ2aWNloIIOzzCCBnEwggRZoAMCAQICCmEJgSoAAAAAAAIwDQYJKoZIhvcN
# AQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD
# VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAw
# BgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEw
# MB4XDTEwMDcwMTIxMzY1NVoXDTI1MDcwMTIxNDY1NVowfDELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgUENBIDIwMTAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCp
# HQ28dxGKOiDs/BOX9fp/aZRrdFQQ1aUKAIKF++18aEssX8XD5WHCdrc+Zitb8BVT
# JwQxH0EbGpUdzgkTjnxhMFmxMEQP8WCIhFRDDNdNuDgIs0Ldk6zWczBXJoKjRQ3Q
# 6vVHgc2/JGAyWGBG8lhHhjKEHnRhZ5FfgVSxz5NMksHEpl3RYRNuKMYa+YaAu99h
# /EbBJx0kZxJyGiGKr0tkiVBisV39dx898Fd1rL2KQk1AUdEPnAY+Z3/1ZsADlkR+
# 79BL/W7lmsqxqPJ6Kgox8NpOBpG2iAg16HgcsOmZzTznL0S6p/TcZL2kAcEgCZN4
# zfy8wMlEXV4WnAEFTyJNAgMBAAGjggHmMIIB4jAQBgkrBgEEAYI3FQEEAwIBADAd
# BgNVHQ4EFgQU1WM6XIoxkPNDe3xGG8UzaFqFbVUwGQYJKwYBBAGCNxQCBAweCgBT
# AHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgw
# FoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDov
# L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0
# XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0
# cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAx
# MC0wNi0yMy5jcnQwgaAGA1UdIAEB/wSBlTCBkjCBjwYJKwYBBAGCNy4DMIGBMD0G
# CCsGAQUFBwIBFjFodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vUEtJL2RvY3MvQ1BT
# L2RlZmF1bHQuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAFAAbwBs
# AGkAYwB5AF8AUwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4IC
# AQAH5ohRDeLG4Jg/gXEDPZ2joSFvs+umzPUxvs8F4qn++ldtGTCzwsVmyWrf9efw
# eL3HqJ4l4/m87WtUVwgrUYJEEvu5U4zM9GASinbMQEBBm9xcF/9c+V4XNZgkVkt0
# 70IQyK+/f8Z/8jd9Wj8c8pl5SpFSAK84Dxf1L3mBZdmptWvkx872ynoAb0swRCQi
# PM/tA6WWj1kpvLb9BOFwnzJKJ/1Vry/+tuWOM7tiX5rbV0Dp8c6ZZpCM/2pif93F
# SguRJuI57BlKcWOdeyFtw5yjojz6f32WapB4pm3S4Zz5Hfw42JT0xqUKloakvZ4a
# rgRCg7i1gJsiOCC1JeVk7Pf0v35jWSUPei45V3aicaoGig+JFrphpxHLmtgOR5qA
# xdDNp9DvfYPw4TtxCd9ddJgiCGHasFAeb73x4QDf5zEHpJM692VHeOj4qEir995y
# fmFrb3epgcunCaw5u+zGy9iCtHLNHfS4hQEegPsbiSpUObJb2sgNVZl6h3M7COaY
# LeqN4DMuEin1wC9UJyH3yKxO2ii4sanblrKnQqLJzxlBTeCG+SqaoxFmMNO7dDJL
# 32N79ZmKLxvHIa9Zta7cRDyXUHHXodLFVeNp3lfB0d4wwP3M5k37Db9dT+mdHhk4
# L7zPWAUu7w2gUDXa7wknHNWzfjUeCLraNtvTX4/edIhJEjCCBNowggPCoAMCAQIC
# EzMAAACxcRN533X2NcgAAAAAALEwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTAwHhcNMTYwOTA3MTc1NjU3WhcNMTgwOTA3MTc1NjU3
# WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjENMAsGA1UE
# CxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNOOkIxQjctRjY3Ri1GRUMy
# MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIBIjANBgkq
# hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqqQklG1Y1lu8ob0P7deumuRn4JvRi2GE
# rmK94vgbnWPmd0j/9arA7539HD1dpG1uhYbmnAxc+qsuvMM0fpEvttTK4lZSU7ss
# 5rJfWmbFn/J8kSGI8K9iBaB6hQkJuIX4si9ppNr9R3oZI3HbJ/yRkKUPk4hozpY6
# CkehRc0/Zfu6tQiyqI7mClXYZTXjw+rLsh3/gdBvYDd38zFBllaf+3uimKQgUTXG
# jbKfqZZk3tEU3ibWVPUxAmmxlG3sWTlXmU31fCw/6TVzGg251lq+Q46OjbeH9vB2
# TOcqEso4Nai3J1CdMAYUdlelVVtgQdIx/c+5Hvrw0Y6W7uGBAWnW5wIDAQABo4IB
# GzCCARcwHQYDVR0OBBYEFE5XPfeLLhRLV7L8Il7Tz7cnRBA7MB8GA1UdIwQYMBaA
# FNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9j
# cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1RpbVN0YVBDQV8y
# MDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6
# Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3RhUENBXzIwMTAt
# MDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJ
# KoZIhvcNAQELBQADggEBAHPujfu0W8PBTpjfYaPrAKIBLKcljT4+YnWbbgGvmXU8
# OvIUDBkkv8gNGGHRO5DSySaCARIzgn2yIheAqh6GwM2yKrfb4eVCYPe1CTlCseS5
# TOv+Tn/95mXj+NxTqvuNmrhgCVr0CQ7b3xoKcwDcQbg7TmerDgbIv2k7cEqbYbU/
# B3MtSX8Zjjf0ZngdKoX0JYkAEDbZchOrRiUtDJItegPKZPf6CjeHYjrmKwvTOVCz
# v3lW0uyh1yb/ODeRH+VqENSHCboFiEiq9KpKMOpek1VvQhmI2KbTlRvK869gj1Nw
# uUHH8c3WXu4A0X1+CBmU8t0gvd/fFlQvw04veKWh986hggN4MIICYAIBATCB46GB
# uaSBtjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjENMAsG
# A1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNOOkIxQjctRjY3Ri1G
# RUMyMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiUKAQEw
# CQYFKw4DAhoFAAMVADq635MoZeR60+ej9uKnRG5YqlPSoIHCMIG/pIG8MIG5MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYDVQQLEwRNT1BS
# MScwJQYDVQQLEx5uQ2lwaGVyIE5UUyBFU046NERFOS0wQzVFLTNFMDkxKzApBgNV
# BAMTIk1pY3Jvc29mdCBUaW1lIFNvdXJjZSBNYXN0ZXIgQ2xvY2swDQYJKoZIhvcN
# AQEFBQACBQDdWK0XMCIYDzIwMTcwOTA1MDQ1NjU1WhgPMjAxNzA5MDYwNDU2NTVa
# MHYwPAYKKwYBBAGEWQoEATEuMCwwCgIFAN1YrRcCAQAwCQIBAAIBIAIB/zAHAgEA
# AgIY4zAKAgUA3Vn+lwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMB
# oAowCAIBAAIDFuNgoQowCAIBAAIDB6EgMA0GCSqGSIb3DQEBBQUAA4IBAQA+ZEd4
# ooPHiKXaGoIJl7xct64OrgoneiQ1Q/WdfJxEl3cjN5B2vRUuQX+6hq5knZBRNSvQ
# hFHIumBL52FqmEl0yu4JrEcavJhNtK+rZYIdiYW/IJN+eYjR7Ca7UiPnqC0AovLC
# q7CH9Pa2LZrFMTw3OEfjCa5O4ZZ2eVaBP0Ts30GgU+BVK7RQ1OnnV3rRL3vwFs9m
# 4RHh92aifGO3xwvmONclucmsupzvizd0YzkG5kFkIj38HaPpRKiR+/z1e1s/qQ5W
# 90R5AVKCgBS/r47n+pjTrHtHKX65tYUnd1FCED51VBM/yI372NQ/ts1ncTv5dpIN
# p/KxiiIUcI+/YNj1MYIC9TCCAvECAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# UENBIDIwMTACEzMAAACxcRN533X2NcgAAAAAALEwDQYJYIZIAWUDBAIBBQCgggEy
# MBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQg2moi
# ZQW4dzeiSQDA0VeE2DeB/Cwgn8IOLGH/0NKFyE4wgeIGCyqGSIb3DQEJEAIMMYHS
# MIHPMIHMMIGxBBQ6ut+TKGXketPno/bip0RuWKpT0jCBmDCBgKR+MHwxCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m
# dCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAAsXETed919jXIAAAAAACxMBYEFF50
# WhcZSLtM19KqNcuqdWzh7UwDMA0GCSqGSIb3DQEBCwUABIIBAG4jEGxIa8zB4J3N
# VJEgYCmwW+6264BmuHXdcIliFkDSosRbONnBxyZE8TJtdsTw98FOO1OH5nVdQTqC
# 4Q//T8PgVFDrbmznYTW+LJw5i1hCUfJJJ724zdvgFduFQA0CCitXvPkHOC9FHidD
# mSf1kZ0waLRCM3F2Sf8X6u7Y+nrVOV7RcbRqJpESfKnWxLJU7fnXy6ekbfTJrqO1
# dGvhbkexJmDyQnqkwroWdtU54MtfXmweW1Oht81LJybbYlyz64RYjWWbAo4j2ILR
# M2BI5yBHCDSRpMPPl3d8+vRCEt49ENNYOqmiFvEq+X/ezawTo0uJOvMPK4zLk3jF
# w33xptQ=
# SIG # End signature block