Framework/Core/SubscriptionSecurity/ARMPolicy.ps1

using namespace System.Management.Automation
Set-StrictMode -Version Latest 

# Class to implement Subscription ARM Policy controls
class ARMPolicy: CommandBase
{    
    hidden [PSObject[]] $Policy = $null;
    
    hidden [PSObject[]] $ApplicableARMPolicies = $null;
    #hidden [PSObject[]] $PolicyAssignments = $null;

    ARMPolicy([string] $subscriptionId, [InvocationInfo] $invocationContext, [string] $tags): 
        Base($subscriptionId, $invocationContext)
    { 
        $this.Policy = [array] $this.LoadServerConfigFile("Subscription.ARMPolicies.json"); 
        $this.FilterTags = $this.ConvertToStringArray($tags);
    }

    hidden [PSObject[]] GetApplicableARMPolices()
    {
        if($null -eq $this.ApplicableARMPolicies)
        {
            $this.ApplicableARMPolicies = @();

            $subscriptionId = $this.SubscriptionContext.SubscriptionId;
            if(($this.FilterTags | Measure-Object).Count -ne 0)
            {
                $this.Policy | 
                    ForEach-Object {
                        $currentItem = $_;
                        if(($currentItem.Tags | Where-Object { $this.FilterTags -Contains $_ } | Measure-Object).Count -ne 0)
                        {
                            # Resolve the value of SubscriptionId
                            $currentItem.Scope = $global:ExecutionContext.InvokeCommand.ExpandString($currentItem.Scope);
                            if([string]::IsNullOrWhiteSpace($currentItem.Scope))
                            {
                                $currentItem.Scope = "/subscriptions/$subscriptionId"
                            }

                            $this.ApplicableARMPolicies  += $currentItem;
                        }
                    }
            }
        }
            
        return $this.ApplicableARMPolicies;
    }

    [MessageData[]] SetARMPolicy()
    {    
        [MessageData[]] $messages = @();
        if(($this.Policy | Measure-Object).Count -ne 0)
        {
            if($this.GetApplicableARMPolices() -ne 0)
            {
                $startMessage = [MessageData]::new("Processing AzSDK ARM policies. Total policies: $($this.GetApplicableARMPolices().Count)");
                $messages += $startMessage;
                $this.PublishCustomMessage($startMessage);
                $this.PublishCustomMessage("Note: Configuring ARM policies can take few minutes depending on number of policies to be processed...", [MessageType]::Warning);                

                $disabledPolicies = $this.GetApplicableARMPolices() | Where-Object { -not $_.Enabled };
                if(($disabledPolicies | Measure-Object).Count -ne 0)
                {
                    $disabledMessage = "Found ARM policies which are disabled. Total disabled policies: $($disabledPolicies.Count)";
                    $messages += [MessageData]::new($disabledMessage, $disabledPolicies);
                    $this.PublishCustomMessage($disabledMessage, [MessageType]::Warning);
                }

                $enabledPolicies = @();
                $enabledPolicies += $this.GetApplicableARMPolices() | Where-Object { $_.Enabled };
                if($enabledPolicies.Count -ne 0)
                {
                    $messages += [MessageData]::new([Constants]::SingleDashLine + "`r`nAdding following ARM policies to the subscription. Total policies: $($enabledPolicies.Count)", $enabledPolicies);                                            
                    
                    [Helpers]::RegisterResourceProviderIfNotRegistered("Microsoft.Scheduler");

                    $errorCount = 0;
                    $currentCount = 0;
                    $enabledPolicies | ForEach-Object {
                        $policyName = $_.PolicyDefinitionName;
                        $currentCount += 1;
                        # Add ARM policy
                        try
                        {
                            $armPolicy = New-AzureRmPolicyDefinition -Name $_.PolicyDefinitionName -Description $_.Description -Policy ([string]$_.PolicyDefinition) -ErrorAction Stop 
                            Start-Sleep -Seconds 10
                            New-AzureRmPolicyAssignment -Name $_.PolicyDefinitionName -PolicyDefinition $armPolicy -Scope $_.Scope -ErrorAction Stop | Out-Null
                        }
                        catch
                        {
                            $messages += [MessageData]::new("Error while adding ARM policy [$policyName] to the subscription", $_, [MessageType]::Error);
                            $errorCount += 1;
                        }

                        $this.CommandProgress($enabledPolicies.Count, $currentCount, 2);
                    };

                    [MessageData[]] $resultMessages = @();
                    if($errorCount -eq 0)
                    {
                        $resultMessages += [MessageData]::new("All AzSDK ARM policies have been added to the subscription successfully`r`n" + [Constants]::SingleDashLine, [MessageType]::Update);
                    }
                    elseif($errorCount -eq $enabledPolicies.Count)
                    {
                        $resultMessages += [MessageData]::new("No AzSDK ARM policies were added to the subscription due to an error. Please add the ARM policies manually.`r`n" + [Constants]::SingleDashLine, [MessageType]::Error);
                    }
                    else
                    {
                        $resultMessages += [MessageData]::new("$errorCount/$($enabledPolicies.Count) ARM polic(ies) have not been added to the subscription. Please add the ARM policies manually.", [MessageType]::Error);
                        $resultMessages += [MessageData]::new("$($enabledPolicies.Count - $errorCount)/$($enabledPolicies.Count) ARM polic(ies) have been added to the subscription successfully`r`n" + [Constants]::SingleDashLine, [MessageType]::Update);
                    }
                    $messages += $resultMessages;
                    $this.PublishCustomMessage($resultMessages);
                }
            }
            else
            {
                $this.PublishCustomMessage("No ARM policies have been found that matches the specified tags. Tags:[$([string]::Join(",", $this.FilterTags))].", [MessageType]::Warning);
            }
        }
        else
        {
            $this.PublishCustomMessage("No ARM policies found in the ARM policy file", [MessageType]::Warning);
        }

        return $messages;
    }

    [MessageData[]] RemoveARMPolicy()
    {    
        [MessageData[]] $messages = @();
        if(($this.Policy | Measure-Object).Count -ne 0)
        {
            if($this.GetApplicableARMPolices() -ne 0)
            {
                $startMessage = [MessageData]::new("Processing ARM policies. Tags:[$([string]::Join(",", $this.FilterTags))]. Total policies: $($this.GetApplicableARMPolices().Count)");
                $messages += $startMessage;
                $this.PublishCustomMessage($startMessage);
                $this.PublishCustomMessage("Note: Removing ARM policies can take few minutes depending on number of policies to be processed...", [MessageType]::Warning);                

                $disabledPolicies = $this.GetApplicableARMPolices() | Where-Object { -not $_.Enabled };
                if(($disabledPolicies | Measure-Object).Count -ne 0)
                {
                    $disabledMessage = "Found ARM policies which are disabled and will not be removed. Total disabled policies: $($disabledPolicies.Count)";
                    $messages += [MessageData]::new($disabledMessage, $disabledPolicies);
                    $this.PublishCustomMessage($disabledMessage, [MessageType]::Warning);
                }

                $currentPolicies = @();
                $currentPolicies += Get-AzureRmPolicyAssignment | Select-Object -Property Name | Select-Object -ExpandProperty Name
    
                $enabledPolicies = @();
                if($currentPolicies.Count -ne 0)
                {
                    $enabledPolicies += $this.GetApplicableARMPolices() | Where-Object { $_.Enabled -and $currentPolicies -contains $_.policyDefinitionName };
                }
                
                if($enabledPolicies.Count -ne 0)
                {
                    $messages += [MessageData]::new([Constants]::SingleDashLine + "`r`nRemoving following ARM policies from the subscription. Total policies: $($enabledPolicies.Count)", $enabledPolicies);                                            

                    $errorCount = 0;
                    $currentCount = 0;
                    $enabledPolicies | ForEach-Object {
                        $policyName = $_.PolicyDefinitionName;
                        $currentCount += 1;
                        # Remove ARM policy
                        try
                        {
                            Remove-AzureRmPolicyAssignment -Name $_.PolicyDefinitionName -Scope $_.Scope -ErrorAction Stop | Out-Null
                            Start-Sleep -Seconds 10
                            Remove-AzureRmPolicyDefinition -Name $_.PolicyDefinitionName -Force -ErrorAction Stop | Out-Null           
                        }
                        catch
                        {
                            $messages += [MessageData]::new("Error while removing ARM policy [$policyName] from the subscription", $_, [MessageType]::Error);
                            $errorCount += 1;
                        }

                        $this.CommandProgress($enabledPolicies.Count, $currentCount, 2);
                    };

                    [MessageData[]] $resultMessages = @();
                    if($errorCount -eq 0)
                    {
                        $resultMessages += [MessageData]::new("All ARM policies have been removed from the subscription successfully`r`n" + [Constants]::SingleDashLine, [MessageType]::Update);
                    }
                    elseif($errorCount -eq $enabledPolicies.Count)
                    {
                        $resultMessages += [MessageData]::new("No ARM policies have been removed from the subscription due to error occurred. Please remove the ARM policies manually.`r`n" + [Constants]::SingleDashLine, [MessageType]::Error);
                    }
                    else
                    {
                        $resultMessages += [MessageData]::new("$errorCount/$($enabledPolicies.Count) ARM polic(ies) have not been removed from the subscription. Please remove the ARM policies manually.", [MessageType]::Error);
                        $resultMessages += [MessageData]::new("$($enabledPolicies.Count - $errorCount)/$($enabledPolicies.Count) ARM polic(ies) have been removed from the subscription successfully`r`n" + [Constants]::SingleDashLine, [MessageType]::Update);
                    }
                    $messages += $resultMessages;
                    $this.PublishCustomMessage($resultMessages);
                }
                else
                {
                    $noPolicyMessage = [MessageData]::new("No ARM policies have been configured by AzSDK on the subscription. No ARM policies have been removed. ", [MessageType]::Warning);
                    $messages += $noPolicyMessage;
                    $this.PublishCustomMessage($noPolicyMessage);
                }
            }
            else
            {
                $this.PublishCustomMessage("No ARM policies have been found that matches the specified tags. Tags:[$([string]::Join(",", $this.FilterTags))].", [MessageType]::Warning);
            }
        }
        else
        {
            $this.PublishCustomMessage("No ARM policies found in the ARM policy file", [MessageType]::Warning);
        }
        return $messages;
    }

}

# SIG # Begin signature block
# MIIkAQYJKoZIhvcNAQcCoIIj8jCCI+4CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCFdn0joHqeZlTk
# 0egZfX0yHrqShVgm54XHFQ4hKtN4X6CCDZMwggYRMIID+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
# hkiG9w0BCQQxIgQgV1BYGPW0GUrBD/U3YqmFt0R69RaCPGIOGEyjSz+CcoowRAYK
# KwYBBAGCNwIBDDE2MDSgEoAQAEEAegBTAEQASwAyADUAMqEegBxodHRwczovL2Fr
# YS5tcy9henNka29zc2RvY3MgMA0GCSqGSIb3DQEBAQUABIIBADfTBQKrZ8etYMpx
# 1xq2vfJvB8lJ9iy0+VOY2fAchv2YGPaf2pXrDHdt11zUnTmr0A40CNlL7LiMs4OC
# nZGXCMPv2JuI+mp3vqD+FhdsRUUr9V+pZ2r6YS71QETPHIdL00quU4sb0jD9x/S5
# cvDEQmLnUkwH4NTwWh/jQjHfb9lL8dz9+Ep8FAp8M00v9Zbt5IEQmpiz6w4gj/og
# EM0iHHVmOobt2eVa+UG5Zomybj0GhZTOznYgFLZwDmVUYF1/Ej7ougT8Svr1+N+h
# GZbQEbb50XeKLSo0Qja7RsRom32iX1ExoFw1pGcUeYAbEJ2J7YKfw5sl7cxyj/pY
# 0gvaV+ehghNMMIITSAYKKwYBBAGCNwMDATGCEzgwghM0BgkqhkiG9w0BBwKgghMl
# MIITIQIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBPAYLKoZIhvcNAQkQAQSgggErBIIB
# JzCCASMCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQgPsP+ez1kyYa/
# n5l7DdkvMrX6DjFUTN8F6bLOO4I+IXcCBlmSONxQ7xgSMjAxNzA5MDUwOTM3MDku
# NThaMAcCAQGAAgH0oIG5pIG2MIGzMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMQ0wCwYDVQQLEwRNT1BSMScwJQYDVQQLEx5uQ2lwaGVyIERTRSBF
# U046N0QyRS0zNzgyLUIwRjcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFNlcnZpY2Wggg7QMIIGcTCCBFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0B
# AQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAG
# A1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAw
# HhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzET
# MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV
# TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1T
# dGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkd
# Dbx3EYo6IOz8E5f1+n9plGt0VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMn
# BDEfQRsalR3OCROOfGEwWbEwRA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq
# 9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8
# RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v
# 0Ev9buWayrGo8noqCjHw2k4GkbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN
# /LzAyURdXhacAQVPIk0CAwEAAaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0G
# A1UdDgQWBBTVYzpcijGQ80N7fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMA
# dQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAW
# gBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8v
# Y3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRf
# MjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRw
# Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEw
# LTA2LTIzLmNydDCBoAYDVR0gAQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYI
# KwYBBQUHAgEWMWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMv
# ZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwA
# aQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIB
# AAfmiFEN4sbgmD+BcQM9naOhIW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4
# vceoniXj+bzta1RXCCtRgkQS+7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3Tv
# QhDIr79/xn/yN31aPxzymXlKkVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8
# z+0DpZaPWSm8tv0E4XCfMkon/VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVK
# C5Em4jnsGUpxY517IW3DnKOiPPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhqu
# BEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF
# 0M2n0O99g/DhO3EJ3110mCIIYdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+
# YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt
# 6o3gMy4SKfXAL1QnIffIrE7aKLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0Mkvf
# Y3v1mYovG8chr1m1rtxEPJdQcdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgv
# vM9YBS7vDaBQNdrvCScc1bN+NR4Iuto229Nfj950iEkSMIIE2jCCA8KgAwIBAgIT
# MwAAAKJMjh3aqSF8hAAAAAAAojANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt
# ZS1TdGFtcCBQQ0EgMjAxMDAeFw0xNjA5MDcxNzU2NDlaFw0xODA5MDcxNzU2NDla
# MIGzMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYDVQQL
# EwRNT1BSMScwJQYDVQQLEx5uQ2lwaGVyIERTRSBFU046N0QyRS0zNzgyLUIwRjcx
# JTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggEiMA0GCSqG
# SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmAXcFe8N9DnZovcaog8aCewFwqLBqhHJP
# VI5tvmcgar7sLd0vR3Hhkv10Ymu+dNuFNts0yMdpCuY2EklAR7hBNFliPfETp64J
# ASjRFFhjHzmwaDYE2FnaTVvgkXES/EGzDc7BcqEVJvbzmVvo4IquEHWqOdfxDvIJ
# wTF1DCkqKd3sjjcq32uq4zK42E17yHEQkMG+OoYZC+jprR+4NCOtYYyWLvs+TC9C
# ZcYLHrGwWMJrm+fPiwTHk0Gd5nm45feWV9yAxQUFAKZBIjcW+bTrR6wvOa3QxdMM
# RNcJW2nRCfMDK2MnWgeQ9O+MozMljTcsPyWZs/MVPqaS6vlRGOXVAgMBAAGjggEb
# MIIBFzAdBgNVHQ4EFgQUbrvZwcLaFrB8rcJTf+fQFxM9vFcwHwYDVR0jBBgwFoAU
# 1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2Ny
# bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljVGltU3RhUENBXzIw
# MTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNUaW1TdGFQQ0FfMjAxMC0w
# Ny0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkq
# hkiG9w0BAQsFAAOCAQEANZlXbTP2SJ2eQdzMqWDuXEdIVBEKhrlXP6dtmI7KfGmm
# apaDzjmyvWQOmTC7vtzgdYJrQinhhUSOMXtzaFl5mJ1XtBYH/KIpvKNwgiEmHWVL
# GeaJKlXNr7qSat7ImgkCWyUWl8eruVra9POgG4JwqkrGRrV6gMRiQoP3cVpkKyb9
# 844jC04W8hvy0DUKQ9o886kakYrENXjZEKhjBqNkf6y/KO6oHBV4j6D3wnHF2LiS
# zqqXcjlMD2hkRE7KhuUty7ICsSg9/Rm/ANZI098NtO0MJJBFFLyVeToD7GOGPDTu
# cbI9Lmp5kIK26xsaWbuvi4t58an9SjHyPGobFV06xqGCA3kwggJhAgEBMIHjoYG5
# pIG2MIGzMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYD
# VQQLEwRNT1BSMScwJQYDVQQLEx5uQ2lwaGVyIERTRSBFU046N0QyRS0zNzgyLUIw
# RjcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiJQoBATAJ
# BgUrDgMCGgUAAxUAXi8X6XGE0jLL7NdeSjv4TreH6fWggcIwgb+kgbwwgbkxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIx
# JzAlBgNVBAsTHm5DaXBoZXIgTlRTIEVTTjo1N0Y2LUMxRTAtNTU0QzErMCkGA1UE
# AxMiTWljcm9zb2Z0IFRpbWUgU291cmNlIE1hc3RlciBDbG9jazANBgkqhkiG9w0B
# AQUFAAIFAN1YrWwwIhgPMjAxNzA5MDUwNDU4MjBaGA8yMDE3MDkwNjA0NTgyMFow
# dzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA3VitbAIBADAKAgEAAgIOeQIB/zAHAgEA
# AgIYtDAKAgUA3Vn+7AIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMB
# oAowCAIBAAIDFuNgoQowCAIBAAIDB6EgMA0GCSqGSIb3DQEBBQUAA4IBAQByYaME
# FOv6bl13h4QN1zZRQWbgCX8IYfNliB04Uq99nX9ieycfowy6hSADAbr4SERbIwOg
# VRuzI0Z1TJdc315A8OaNPgaTVOXtxAeAcQpWaoSWBUDIMhITdXcK+piXKkJqxiWK
# 4639oT4SIG16tWaQ88QH5cXPUz5YwUSOMD+X/3NNCP1diN7LjjXmjHn+ThNPTYEZ
# Sv7Q5xTX/sHMvcKoQdGlqX0RLqLv7yMWybzGCLuJ8xlJNV4WVzDQDM7fz5hRztP6
# i2GNafnQuXQg1hYgTW4cw/oz6EY318PxMrRZ2Foo/n2ltwyl313eSMb6w16CBKFv
# BxU8mI3NxUhoZuPEMYIC9TCCAvECAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# UENBIDIwMTACEzMAAACiTI4d2qkhfIQAAAAAAKIwDQYJYIZIAWUDBAIBBQCgggEy
# MBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgaZV0
# 665h8w2m0puw/nIT6O4tQAqqqhCDK/2/lHm+3ZEwgeIGCyqGSIb3DQEJEAIMMYHS
# MIHPMIHMMIGxBBReLxfpcYTSMsvs115KO/hOt4fp9TCBmDCBgKR+MHwxCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m
# dCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAAokyOHdqpIXyEAAAAAACiMBYEFMwE
# SrWjo1vDkQE04SippZzOshpAMA0GCSqGSIb3DQEBCwUABIIBAJhFeAm3Su3mgk9c
# Ke9cNL2UklvRUZUKb4MbyqwZepTF0F9rTa73YhnhJyEe+dbBBxlc7Z0WfnzdF4rW
# hL+D0h58wbqdkWoI38q5qQasYjF9NPWmbWsbxte6pzn8FUJEV26Rr2RJX896VZAc
# yisXW0s3hxmFolOoHugDPfGhxLpVW5ygxxJ+3AI4/eqQHccGQAvl+BMZ/lXWPK+e
# sSoXGeZI01zcGvxfzHAUtZyM9DbZ/AJGfty+lwgCU267N3X55lkfSFnyYIpq8Mj6
# vKOfChNKu9sigbKCeZe2PdwYjaYnbpITneXZJ/KVpJlIVG9ncI3R/T6gLXbHP2Dm
# J0U2Q5g=
# SIG # End signature block