Framework/Core/SubscriptionSecurity/RBAC.ps1
using namespace Microsoft.Azure.Commands.Resources.Models.Authorization using namespace System.Management.Automation Set-StrictMode -Version Latest # Class to implement Subscription RBAC controls class RBAC: CommandBase { hidden [SubscriptionRBAC] $Policy = $null; hidden [PSRoleAssignment[]] $RoleAssignments = $null; hidden [ActiveRBACAccount[]] $ApplicableActiveAccounts = $null; hidden [ActiveRBACAccount[]] $MissingActiveAccounts = $null; hidden [RBACAccountRoleMapping[]] $MatchedActiveAccounts = $null; hidden [RBACAccountRoleMapping[]] $MatchedDeprecatedAccounts = $null; RBAC([string] $subscriptionId, [InvocationInfo] $invocationContext, [string] $tags): Base($subscriptionId, $invocationContext) { $this.Policy = [SubscriptionRBAC] $this.LoadServerConfigFile("Subscription.RBAC.json"); $this.FilterTags = $this.ConvertToStringArray($tags); } hidden [PSRoleAssignment[]] GetRoleAssignments() { if(-not $this.RoleAssignments) { $this.RoleAssignments = [RoleAssignmentHelper]::GetAzSDKRoleAssignment($true, $true); } return $this.RoleAssignments; } hidden [ActiveRBACAccount[]] GetApplicableActiveAccounts() { if($null -eq $this.ApplicableActiveAccounts) { $this.ApplicableActiveAccounts = @(); $subscriptionId = $this.SubscriptionContext.SubscriptionId; if(($this.FilterTags | Measure-Object).Count -ne 0) { $this.Policy.ValidActiveAccounts | 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.ApplicableActiveAccounts += $currentItem; } } } } return $this.ApplicableActiveAccounts; } hidden [void] LoadActiveAccountStatus() { $this.MissingActiveAccounts = @(); $this.MatchedActiveAccounts = @(); $this.GetApplicableActiveAccounts() | Where-Object { $_.Enabled } | ForEach-Object { $currentItem = $_; $matchedAccount = $this.GetRoleAssignments() | Where-Object { ($_.ObjectId -eq $currentItem.ObjectId) -and ($_.Scope -eq $currentItem.Scope) -and ($_.RoleDefinitionName -eq $currentItem.RoleDefinitionName) } | Select-Object -First 1 if($matchedAccount) { $this.MatchedActiveAccounts += [RBACAccountRoleMapping]@{ RoleAssignment = $matchedAccount; RBACAccount = $currentItem; }; } else { $this.MissingActiveAccounts += $currentItem; } }; } hidden [void] LoadDeprecatedAccountStatus() { $this.MatchedDeprecatedAccounts = @(); $this.Policy.DeprecatedAccounts | Where-Object { $_.Enabled } | ForEach-Object { $currentItem = $_; $matchedAccounts = $this.GetRoleAssignments() | Where-Object { ($_.ObjectId -eq $currentItem.ObjectId) }; if(($matchedAccounts | Measure-Object).Count -ne 0) { $matchedAccounts | ForEach-Object { $this.MatchedDeprecatedAccounts += [RBACAccountRoleMapping]@{ RoleAssignment = $_; RBACAccount = $currentItem; }; }; } }; } [MessageData[]] SetRBACAccounts() { [MessageData[]] $messages = @(); # Set Active accounts $nonConfiguredActiveAccounts = $this.GetNonConfiguredActiveAccounts(([ref]$messages)); if($nonConfiguredActiveAccounts.Count -ne 0) { $provisionAccounts = @(); $provisionAccounts += $nonConfiguredActiveAccounts | Where-Object { $_.Type -eq [RBACAccountType]::Provision }; if($provisionAccounts.Count -ne 0) { # Adding Central Accounts $messages += [MessageData]::new([Constants]::SingleDashLine + "`r`nAdding following active accounts to the subscription. Total accounts: $($provisionAccounts.Count)", $provisionAccounts); $errorCount = 0; $currentCount = 0; $provisionAccounts | ForEach-Object { $accountName = $_.Name; $currentCount += 1; # Add account try { $this.PublishCustomMessage("Adding account [$accountName] to the subscription"); New-AzureRmRoleAssignment -ObjectId $_.ObjectId -Scope $_.Scope -RoleDefinitionName $_.RoleDefinitionName -ErrorAction Stop | Out-Null } catch { $messages += [MessageData]::new("Error while adding account [$accountName] to the subscription", $_, [MessageType]::Error); $errorCount += 1; } $this.CommandProgress($provisionAccounts.Count, $currentCount, 5); }; [MessageData[]] $resultMessages = @(); if($errorCount -eq 0) { $resultMessages += [MessageData]::new("All required active accounts have been added to the subscription successfully`r`n" + [Constants]::SingleDashLine, [MessageType]::Update); } elseif($errorCount -eq $provisionAccounts.Count) { $resultMessages += [MessageData]::new("No accounts have been added to the subscription due to error occurred. Please add the accounts manually.`r`n" + [Constants]::SingleDashLine, [MessageType]::Error); } else { $resultMessages += [MessageData]::new("$errorCount/$($provisionAccounts.Count) required active account(s) have not been added to the subscription. Please add the accounts manually.", [MessageType]::Error); $resultMessages += [MessageData]::new("$($provisionAccounts.Count - $errorCount)/$($provisionAccounts.Count) required active account(s) have been added to the subscription successfully`r`n" + [Constants]::SingleDashLine, [MessageType]::Update); } $messages += $resultMessages; $this.PublishCustomMessage($resultMessages); } $nonProvisionAccounts = @(); $nonProvisionAccounts += $nonConfiguredActiveAccounts | Where-Object { $_.Type -ne [RBACAccountType]::Provision }; if($nonProvisionAccounts.Count -ne 0) { $messageText = "Some accounts are not added to the subscription, since the account type is not set to 'Provision' in RBAC policy file. Total accounts: $($nonProvisionAccounts.Count)"; $messages += [MessageData]::new($messageText, $nonProvisionAccounts); $this.PublishCustomMessage($messageText); } } else { } return $messages; } [ActiveRBACAccount[]] GetNonConfiguredActiveAccounts([ref]$messages) { if($this.Policy.ValidActiveAccounts.Count -ne 0) { if($this.GetApplicableActiveAccounts().Count -ne 0) { $startMessage = [MessageData]::new("Processing RBAC rules for adding central accounts. Tags:[$([string]::Join(",", $this.FilterTags))]. Total accounts: $($this.GetApplicableActiveAccounts().Count)"); $messages.Value += $startMessage; $this.PublishCustomMessage($startMessage); $disabledAccounts = $this.GetApplicableActiveAccounts() | Where-Object { -not $_.Enabled }; if(($disabledAccounts | Measure-Object).Count -ne 0) { $disabledMessage = "Found accounts which are disabled. Total disabled accounts: $($disabledAccounts.Count)"; $messages.Value += [MessageData]::new($disabledMessage, $disabledAccounts); $this.PublishCustomMessage($disabledMessage, [MessageType]::Warning); } # Load all properties $this.LoadActiveAccountStatus(); if($this.MatchedActiveAccounts.Count -ne 0) { $messages.Value += [MessageData]::new("Following accounts have correct RBAC configured. Total accounts: $($this.MatchedActiveAccounts.Count)", ($this.MatchedActiveAccounts | Select-Object -Property RoleAssignment)); } if($this.MissingActiveAccounts.Count -ne 0) { $missingAccountsMessage = "Following accounts does not have correct RBAC configured. Total accounts: $($this.MissingActiveAccounts.Count)"; $messages.Value += [MessageData]::new($missingAccountsMessage, $this.MissingActiveAccounts); $this.PublishCustomMessage($missingAccountsMessage); } else { $successMessage = [MessageData]::new("All required accounts are correctly configured"); $messages.Value += $successMessage; $this.PublishCustomMessage($successMessage); } return $this.MissingActiveAccounts; } else { $this.PublishCustomMessage("No active accounts have been found that matches the specified tags. Tags:[$([string]::Join(",", $this.FilterTags))].", [MessageType]::Warning); } } else { $this.PublishCustomMessage("No active accounts found in the RBAC policy file", [MessageType]::Warning); } return @(); } [RBACAccountRoleMapping[]] GetMatchedDeprecatedAccounts([ref]$messages) { if($this.Policy.DeprecatedAccounts -and $this.Policy.DeprecatedAccounts.Count -ne 0) { #$this.PublishCustomMessage("Found deprecated accounts in the RBAC policy file"); $startMessage = [MessageData]::new("Processing RBAC rules for removing deprecated central accounts. Total accounts: $($this.Policy.DeprecatedAccounts.Count)"); $messages.Value += $startMessage; #$this.PublishCustomMessage($startMessage); $disabledAccounts = $this.Policy.DeprecatedAccounts | Where-Object { -not $_.Enabled }; if(($disabledAccounts | Measure-Object).Count -ne 0) { $disabledMessage = "Found accounts which are disabled. Total disabled accounts: $($disabledAccounts.Count)"; $messages.Value += [MessageData]::new($disabledMessage, $disabledAccounts); $this.PublishCustomMessage($disabledMessage, [MessageType]::Warning); } # Load all properties $this.LoadDeprecatedAccountStatus(); if($this.MatchedDeprecatedAccounts.Count -ne 0) { $messages.Value += [MessageData]::new("Following deprecated accounts must be removed from the subscription. Total accounts: $($this.MatchedDeprecatedAccounts.Count)", ($this.MatchedDeprecatedAccounts | Select-Object -Property RoleAssignment)); } else { $resultMessage = [MessageData]::new("No deprecated accounts found on the subscription"); $messages.Value += $resultMessage; $this.PublishCustomMessage($resultMessage); } return $this.MatchedDeprecatedAccounts; } else { $this.PublishCustomMessage("No deprecated accounts found in the RBAC policy file"); } return @(); } [MessageData[]] RemoveRBACAccounts() { [MessageData[]] $messages = @(); if($this.Policy.ValidActiveAccounts.Count -ne 0) { if($this.GetApplicableActiveAccounts().Count -ne 0) { $startMessage += [MessageData]::new("Processing RBAC rules for removing central accounts. Tags:[$([string]::Join(",", $this.FilterTags))]. Total accounts: $($this.GetApplicableActiveAccounts().Count)"); $messages += $startMessage; $this.PublishCustomMessage($startMessage); $disabledAccounts = $this.GetApplicableActiveAccounts() | Where-Object { -not $_.Enabled }; if(($disabledAccounts | Measure-Object).Count -ne 0) { $disabledMessage = "Found accounts which are disabled. Total disabled accounts: $($disabledAccounts.Count)"; $messages += [MessageData]::new($disabledMessage, $disabledAccounts); $this.PublishCustomMessage($disabledMessage, [MessageType]::Warning); } # Load all properties $this.LoadActiveAccountStatus(); $messages += $this.RemoveRoleAssignments($this.MatchedActiveAccounts); } else { $this.PublishCustomMessage("No active accounts have been found that matches the specified tags. Tags:[$([string]::Join(",", $this.FilterTags))].", [MessageType]::Warning); } } else { $this.PublishCustomMessage("No active accounts found in the RBAC policy file", [MessageType]::Warning); } # Remove deprecated accounts $depAccounts = $this.GetMatchedDeprecatedAccounts(([ref]$messages)); $messages += $this.RemoveRBACAccounts($depAccounts); return $messages; } hidden [MessageData[]] RemoveRoleAssignments([RBACAccountRoleMapping[]] $rbacRoleMapping) { [MessageData[]] $messages = @(); if($rbacRoleMapping.Count -ne 0) { $messages += [MessageData]::new("Total matching accounts found in the subscription: $($rbacRoleMapping.Count)"); $provisionAccounts = @(); $provisionAccounts += $rbacRoleMapping | Where-Object { $_.RBACAccount.Type -eq [RBACAccountType]::Provision }; $messages += $this.RemoveRBACAccounts($provisionAccounts); $nonProvisionAccounts = @(); $nonProvisionAccounts += $rbacRoleMapping | Where-Object { $_.RBACAccount.Type -ne [RBACAccountType]::Provision }; if($nonProvisionAccounts.Count -ne 0) { $messageText = "Some accounts were not removed from the subscription, since the account type is not set to 'Provision' in RBAC policy file. Total accounts: $($nonProvisionAccounts.Count)"; $messages += [MessageData]::new($messageText, ($nonProvisionAccounts | Select-Object -Property RoleAssignment)); #$this.PublishCustomMessage($messageText); } } return $messages; } hidden [MessageData[]] RemoveRBACAccounts([RBACAccountRoleMapping[]] $rbacRoleMapping) { [MessageData[]] $messages = @(); if($rbacRoleMapping.Count -ne 0) { # Removing Central Accounts $messages += [MessageData]::new([Constants]::SingleDashLine + "`r`nRemoving following accounts from the subscription. Total accounts: $($rbacRoleMapping.Count)", ($rbacRoleMapping | Select-Object -Property RoleAssignment)); $errorCount = 0; $currentCount = 0; $rbacRoleMapping | ForEach-Object { $accountName = $_.RoleAssignment.DisplayName; $currentCount += 1; if(-not [string]::IsNullOrWhiteSpace($_.RoleAssignment.SignInName)) { $accountName += " ($($_.RoleAssignment.SignInName))"; } # Remove account try { $this.PublishCustomMessage("Removing account [$accountName] from the subscription"); Remove-AzureRmRoleAssignment -ObjectId $_.RoleAssignment.ObjectId -Scope $_.RoleAssignment.Scope -RoleDefinitionId $_.RoleAssignment.RoleDefinitionId -ErrorAction Stop | Out-Null } catch { $messages += [MessageData]::new("Error while removing account [$accountName] from the subscription", $_, [MessageType]::Error); $errorCount += 1; } $this.CommandProgress($rbacRoleMapping.Count, $currentCount, 5); }; [MessageData[]] $resultMessages = @(); if($errorCount -eq 0) { $resultMessages += [MessageData]::new("All matched accounts have been removed from the subscription successfully`r`n" + [Constants]::SingleDashLine, [MessageType]::Update); } elseif($errorCount -eq $rbacRoleMapping.Count) { $resultMessages += [MessageData]::new("No accounts have been removed from the subscription due to error occurred. Please remove the accounts manually.`r`n" + [Constants]::SingleDashLine, [MessageType]::Error); } else { $resultMessages += [MessageData]::new("$errorCount/$($rbacRoleMapping.Count) matched account(s) have not been removed from the subscription. Please remove the accounts manually.", [MessageType]::Error); $resultMessages += [MessageData]::new("$($rbacRoleMapping.Count - $errorCount)/$($rbacRoleMapping.Count) matched account(s) have been removed from the subscription successfully`r`n" + [Constants]::SingleDashLine, [MessageType]::Update); } $messages += $resultMessages; $this.PublishCustomMessage($resultMessages); } return $messages; } } class RBACAccountRoleMapping { hidden [PSRoleAssignment] $RoleAssignment = $null; hidden [RBACAccount] $RBACAccount = $null; } # SIG # Begin signature block # MIIkAgYJKoZIhvcNAQcCoIIj8zCCI+8CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCB5x3yJdY/1YPC5 # 4dUleoGS/FxMyJZoSk+8rkkw41jLxqCCDZMwggYRMIID+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 # xTCCFcECAQEwgZUwfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAA # AI6HkaRXGl/KPgAAAAAAjjANBglghkgBZQMEAgEFAKCBsDAZBgkqhkiG9w0BCQMx # DAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkq # hkiG9w0BCQQxIgQgoluADS8GQf6C88zY80hYfM+bLtMWZJqRtBS6BaCeD/gwRAYK # KwYBBAGCNwIBDDE2MDSgEoAQAEEAegBTAEQASwAyADUAMqEegBxodHRwczovL2Fr # YS5tcy9henNka29zc2RvY3MgMA0GCSqGSIb3DQEBAQUABIIBAJeMvJfIX5npBpbY # 3RWU/6jV/hqyu9TTfBsynnDSzOys6D1s++BspBsfE2WA+LEFvrF3+xC3Gfh5Tg+y # dnHfJ70yXj3JXMz69p6KyhzKSy4T2SB6uvEb0qVhAHPLMzwyatcoDBSvaYkmCktd # NQbjtBQn6jxC5Bb+dJXqv4wauMa/qAlfqji0BoWNVNLJA3xY3JiIVHyZLoEEkJdQ # pk+GzxWbbUx62BHGTbqmMMThWO27549EDjTNE9IsWLGEWIuI8bBxFSMt475amQLY # 7qkD1CQOPriOILdR0CizX3FSqxxC/hubshtwjLkM450bIdAEsKJq4/QdeSIa30b1 # Fnhgn2ShghNNMIITSQYKKwYBBAGCNwMDATGCEzkwghM1BgkqhkiG9w0BBwKgghMm # MIITIgIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBPQYLKoZIhvcNAQkQAQSgggEsBIIB # KDCCASQCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQgaRcRc9uSaIhR # 3WXC0x+Q0bIl+avV5wSwxEl5cEljUswCBlmSOrJ/xBgTMjAxNzA5MDUwOTM3MTAu # Njg5WjAHAgEBgAIB9KCBuaSBtjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0Ug # RVNOOkJCRUMtMzBDQS0yREJFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt # cCBTZXJ2aWNloIIO0DCCBnEwggRZoAMCAQICCmEJgSoAAAAAAAIwDQYJKoZIhvcN # 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 # EzMAAAChpf257qf8np0AAAAAAKEwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTAwHhcNMTYwOTA3MTc1NjQ4WhcNMTgwOTA3MTc1NjQ4 # WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjENMAsGA1UE # CxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNOOkJCRUMtMzBDQS0yREJF # MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIBIjANBgkq # hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm9ABeeYir3p8G3Ue87dn1h3ep94ANNgS # +QfqCHsfTU3KhZR6q3ZrKgdFjVEn07ZdRqUlxmIUeYtPzOYs9eyfTXodNCI2KrjD # 4uzFUO3T/UPBLb/F8PrPzISQ66Kmsm1XoI+5YXDUSc6IL4MuO4FKk7VJSsRlyZaF # 5C/6rOLYVx0z9r4Q58JSGxPg+RQ2qLOb9NsV8PTSa30tuFXOEelW/5TpIQ67kVfM # nBV5cM2OrNPjgZmYww4H39tzxc8pY/U+7DcYenP2JHW1/Mk3lDBXB9WgQBVNCxaw # 5tU3XTzY06u8h5eHelVzS2FDwfMJiJK+zrjlhEo8FjecQc4gl4HICQIDAQABo4IB # GzCCARcwHQYDVR0OBBYEFKtcuYK+cSrVj+DosinP+hvTt/pIMB8GA1UdIwQYMBaA # FNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9j # cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1RpbVN0YVBDQV8y # MDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6 # Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3RhUENBXzIwMTAt # MDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJ # KoZIhvcNAQELBQADggEBACNgTLgFVOlnyb45PBUywxPIswCQfxczm/12L11MErPo # sCSi/rL0H6iyji5OEAdc6Pc0iu40HejhRIb4HtvePRKUh8GaD0Pgm/oUYau26hLj # qohq12V35Qdb0FBT0cVa1CgvKkpReR95OSp3x2HlI38qBdomntVAtuJf3DoTdOU6 # /ar7PwL8K/n4IFJbKMpdsiAo7h0e9IqEvBdS6rMScZosHRtODXjR25MNJF4XiElU # IfzYXCbQ6RPhbMpOvwe4O/nhnC9GDGU6nEWwCadzTCxrttcWY+D8cjiZpgXNMpFB # ol76u9etDnuFy/MPdzt4MtNPlpEUSCPGipeXWB39pUGhggN5MIICYQIBATCB46GB # uaSBtjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV # BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjENMAsG # A1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNOOkJCRUMtMzBDQS0y # REJFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiUKAQEw # CQYFKw4DAhoFAAMVAIKuifW05j8WXCC8F+TBw0DNOetooIHCMIG/pIG8MIG5MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYDVQQLEwRNT1BS # MScwJQYDVQQLEx5uQ2lwaGVyIE5UUyBFU046NTdGNi1DMUUwLTU1NEMxKzApBgNV # BAMTIk1pY3Jvc29mdCBUaW1lIFNvdXJjZSBNYXN0ZXIgQ2xvY2swDQYJKoZIhvcN # AQEFBQACBQDdWK1iMCIYDzIwMTcwOTA1MDQ1ODEwWhgPMjAxNzA5MDYwNDU4MTBa # MHcwPQYKKwYBBAGEWQoEATEvMC0wCgIFAN1YrWICAQAwCgIBAAICC9cCAf8wBwIB # AAICGHYwCgIFAN1Z/uICAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoD # AaAKMAgCAQACAxbjYKEKMAgCAQACAwehIDANBgkqhkiG9w0BAQUFAAOCAQEASpxc # bnQwkaEQw3guLPcoKD7VPE+CLc+YZ+QvqoPBFBxqYH9L7McbMsAjkkpnkLtivNT0 # Ga5eHWnZuP7LUzsrLz9DlQ/Y9SajTkNhcWFKXfe25Kky8pX3RKsHwNUk139FQTIs # PldOqxTHPZof785BWPvVm2za4jcoSJKgloBr2b9nfzIdfMJrTI+Pf2TGKd3a8cwx # mddqY34PblgwYq58X3Xw+/p1dLD7KrQWkmn/Ea2RyRdhaS1DghJNNmj7AKfQkZYn # oMNjicyDnIZAwk905FXMtf0Ovy9GA2rkKgPoZKmRsD0tt6h3bhmeMC89eYW6juUi # kZmuGfAWGY2KSoTW8zGCAvUwggLxAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwAhMzAAAAoaX9ue6n/J6dAAAAAAChMA0GCWCGSAFlAwQCAQUAoIIB # MjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEIAnT # JgGyOpdq1Pr1mAQKHSJo19k30iFdmiqmCgSYXZ4oMIHiBgsqhkiG9w0BCRACDDGB # 0jCBzzCBzDCBsQQUgq6J9bTmPxZcILwX5MHDQM0562gwgZgwgYCkfjB8MQswCQYD # VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe # MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3Nv # ZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAKGl/bnup/yenQAAAAAAoTAWBBSm # Dc2qsbSvLSMHZgss4F9KRUM0TDANBgkqhkiG9w0BAQsFAASCAQCbtiYREISKTofi # v0CWdLiF1Wt6jhYh33vyxyH24iM4/lVGpCRy831DhVXhY3/6AXU/C8T00g0bbbc1 # 9W+Wlba8AeCJBbUvgQs4B8dfuMfD5sJwmV0rzgAtBF/23ZM2aKnJpdT8g3ru2mO2 # B8AlGvQJlTqX1NqfeeHJsa4vVVq48OCwOfiKEjGXyEg6oBfEQbH/eNe+xTv6STK+ # 6kkIDKqhbtqA/xSfrOCb98H5+DAwZ0AfGt9y3l64cGsz7PoMqBRubnYXl+YsGjgc # VTrZeUsCxBCqUcQhez+ZhoC3YT8jNhD/+5OGFOY4q4svnOPett71MWddCY1GKJnC # R2vWF7d2 # SIG # End signature block |