functions/Add-DbaAgDatabase.ps1
function Add-DbaAgDatabase { <# .SYNOPSIS Adds database(s) to an Availability Group on a SQL Server instance. .DESCRIPTION Adds database(s) to an Availability Group on a SQL Server instance. After checking for prerequisites, the commands runs these five steps for every database: * Step 1: Setting seeding mode if needed. - If -SeedingMode is used and the current seeding mode of the replica is not in the desired mode, the seeding mode of the replica is changed. - The seeding mode will not be changed back but stay in this mode. - If the seeding mode is changed to Automatic, the necessary rights to create databases will be granted. * Step 2: Running backup and restore if needed. - Action is only taken for replicas with a desired seeding mode of Manual and where the database does not yet exist. - If -UseLastBackup is used, the restore will be performed based on the backup history of the database. - Otherwise a full and log backup will be taken at the primary and those will be restored at the replica. * Step 3: Add the database to the Availability Group on the primary replica. - This step is skipped, if the database is already part of the Availability Group. * Step 4: Add the database to the Availability Group on the secondary replicas. - This step is skipped for those replicas, where the database is already joined to the Availability Group. * Step 5: Wait for the database to finish joining the Availability Group on the secondary replicas. Use Test-DbaAvailabilityGroup with -AddDatabase to test if all prerequisites are met. If you have special requirements for the setup for the database at the replicas, perform the backup and restore part with Backup-DbaDatabase and Restore-DbaDatabase in advance. Please make sure that the last log backup has been restored before running Add-DbaAgDatabase. .PARAMETER SqlInstance The primary replica of the Availability Group. Server version must be SQL Server version 2012 or higher. .PARAMETER SqlCredential Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. For MFA support, please use Connect-DbaInstance. .PARAMETER Database The database(s) to add. .PARAMETER AvailabilityGroup The name of the Availability Group where the databases will be added. .PARAMETER Secondary Not required - the command will figure this out. But use this parameter if secondary replicas listen on a non default port. This parameter can be used to only add the databases on specific secondary replicas. .PARAMETER SecondarySqlCredential Login to the target instance using alternative credentials. Accepts PowerShell credentials (Get-Credential). Windows Authentication, SQL Server Authentication, Active Directory - Password, and Active Directory - Integrated are all supported. For MFA support, please use Connect-DbaInstance. .PARAMETER InputObject Enables piping from Get-DbaDatabase, Get-DbaDbSharePoint and more. .PARAMETER SeedingMode Specifies how the secondary replica will be initially seeded. Automatic enables direct seeding. This method will seed the secondary replica over the network. This method does not require you to backup and restore a copy of the primary database on the replica. Manual uses full and log backup to initially transfer the data to the secondary replica. The command skips this if the database is found in restoring state at the secondary replica. If not specified, the setting from the availability group replica will be used. Otherwise the setting will be updated. .PARAMETER SharedPath The network share where the backups will be backed up and restored from. Each SQL Server service account must have access to this share. NOTE: If a backup / restore is performed, the backups will be left in tact on the network share. .PARAMETER UseLastBackup Use the last full and log backup of the database. A log backup must be the last backup. .PARAMETER WhatIf Shows what would happen if the command were to run. No actions are actually performed. .PARAMETER Confirm Prompts you for confirmation before executing any changing operations within the command. .PARAMETER EnableException By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting. Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch. .NOTES Tags: AG, HA Author: Chrissy LeMaire (@cl), netnerds.net | Andreas Jordan (@JordanOrdix), ordix.de Website: https://dbatools.io Copyright: (c) 2018 by dbatools, licensed under MIT License: MIT https://opensource.org/licenses/MIT .LINK https://dbatools.io/Add-DbaAgDatabase .EXAMPLE PS C:\> Add-DbaAgDatabase -SqlInstance sql2017a -AvailabilityGroup ag1 -Database db1, db2 -Confirm Adds db1 and db2 to ag1 on sql2017a. Prompts for confirmation. .EXAMPLE PS C:\> Get-DbaDatabase -SqlInstance sql2017a | Out-GridView -Passthru | Add-DbaAgDatabase -AvailabilityGroup ag1 Adds selected databases from sql2017a to ag1 .EXAMPLE PS C:\> Get-DbaDbSharePoint -SqlInstance sqlcluster | Add-DbaAgDatabase -AvailabilityGroup SharePoint Adds SharePoint databases as found in SharePoint_Config on sqlcluster to ag1 on sqlcluster .EXAMPLE PS C:\> Get-DbaDbSharePoint -SqlInstance sqlcluster -ConfigDatabase SharePoint_Config_2019 | Add-DbaAgDatabase -AvailabilityGroup SharePoint Adds SharePoint databases as found in SharePoint_Config_2019 on sqlcluster to ag1 on sqlcluster #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')] param ( [Parameter(ParameterSetName = 'NonPipeline', Mandatory = $true, Position = 0)] [DbaInstanceParameter]$SqlInstance, [Parameter(ParameterSetName = 'NonPipeline')] [PSCredential]$SqlCredential, [Parameter(ParameterSetName = 'NonPipeline', Mandatory = $true)] [Parameter(ParameterSetName = 'Pipeline', Mandatory = $true, Position = 0)] [string]$AvailabilityGroup, [Parameter(ParameterSetName = 'NonPipeline', Mandatory = $true)] [string[]]$Database, [Parameter(ParameterSetName = 'NonPipeline')] [Parameter(ParameterSetName = 'Pipeline')] [DbaInstanceParameter[]]$Secondary, [Parameter(ParameterSetName = 'NonPipeline')] [Parameter(ParameterSetName = 'Pipeline')] [PSCredential]$SecondarySqlCredential, [parameter(ValueFromPipeline, ParameterSetName = 'Pipeline', Mandatory = $true)] [Microsoft.SqlServer.Management.Smo.Database[]]$InputObject, [Parameter(ParameterSetName = 'NonPipeline')] [Parameter(ParameterSetName = 'Pipeline')] [ValidateSet('Automatic', 'Manual')] [string]$SeedingMode, [Parameter(ParameterSetName = 'NonPipeline')] [Parameter(ParameterSetName = 'Pipeline')] [string]$SharedPath, [Parameter(ParameterSetName = 'NonPipeline')] [Parameter(ParameterSetName = 'Pipeline')] [switch]$UseLastBackup, [Parameter(ParameterSetName = 'NonPipeline')] [Parameter(ParameterSetName = 'Pipeline')] [switch]$EnableException ) begin { # We have three while loops, that need a timeout to not loop forever if somethings goes wrong: # while ($agDb.State -ne 'Existing') - should only take milliseconds, so we set a default timeout of one minute # while ($replicaAgDb.State -ne 'Existing') - should only take milliseconds, so we set a default timeout of one minute # while ($stillWaiting) - can take a long time with automatic seeding, but progress is displayed, so we set a default timeout of one day # We will use two timeout configuration values, as we don't want to add more timeout parameters to the command. We will store the timeouts in seconds. # The timout for synchronization can be set to a lower value to end the command even when the synchronization is not finished yet. # The synchronization will continue even the command or the powershell session stops. # Even when the SQL Server instance is restarted, the synchronization will continue after the restart. # Set-DbatoolsConfig -FullName commands.add-dbaagdatabase.timeout.existing -Value 60 # Set-DbatoolsConfig -FullName commands.add-dbaagdatabase.timeout.synchronization -Value 86400 $timeoutExisting = Get-DbatoolsConfigValue -FullName commands.add-dbaagdatabase.timeout.existing -Fallback 60 $timeoutSynchronization = Get-DbatoolsConfigValue -FullName commands.add-dbaagdatabase.timeout.synchronization -Fallback 86400 # While in a while loop, configure the time in milliseconds to wait for the next test: # Set-DbatoolsConfig -FullName commands.add-dbaagdatabase.wait.while -Value 100 $waitWhile = Get-DbatoolsConfigValue -FullName commands.add-dbaagdatabase.wait.while -Fallback 100 # With automatic seeding we add the current seeding progress in verbose output and a progress bar. This can be disabled: # Set-DbatoolsConfig -FullName commands.add-dbaagdatabase.report.seeding -Value $true $reportSeeding = Get-DbatoolsConfigValue -FullName commands.add-dbaagdatabase.report.seeding -Fallback $true } process { # We store information for the progress bar in a hashtable suitable for splatting. $progress = @{ } $progress['Id'] = Get-Random $progress['Activity'] = "Adding database(s) to Availability Group $AvailabilityGroup." $testResult = @( ) foreach ($dbName in $Database) { try { $progress['Status'] = "Test prerequisites for joining database $dbName." Write-Progress @progress $testSplat = @{ SqlInstance = $SqlInstance SqlCredential = $SqlCredential Secondary = $Secondary SecondarySqlCredential = $SecondarySqlCredential AvailabilityGroup = $AvailabilityGroup AddDatabase = $dbName UseLastBackup = $UseLastBackup EnableException = $true } if ($SeedingMode) { $testSplat['SeedingMode'] = $SeedingMode } if ($SharedPath) { $testSplat['SharedPath'] = $SharedPath } $testResult += Test-DbaAvailabilityGroup @testSplat } catch { Stop-Function -Message "Testing prerequisites for joining database $dbName to Availability Group $AvailabilityGroup failed." -ErrorRecord $_ -Continue } } foreach ($db in $InputObject) { try { $progress['Status'] = "Test prerequisites for joining database $($db.Name)." Write-Progress @progress $testSplat = @{ SqlInstance = $db.Parent Secondary = $Secondary SecondarySqlCredential = $SecondarySqlCredential AvailabilityGroup = $AvailabilityGroup AddDatabase = $db.Name UseLastBackup = $UseLastBackup EnableException = $true } if ($SeedingMode) { $testSplat['SeedingMode'] = $SeedingMode } if ($SharedPath) { $testSplat['SharedPath'] = $SharedPath } $testResult += Test-DbaAvailabilityGroup @testSplat } catch { Stop-Function -Message "Testing prerequisites for joining database $($db.Name) to Availability Group $AvailabilityGroup failed." -ErrorRecord $_ -Continue } } Write-Message -Level Verbose -Message "Test for prerequisites returned $($testResult.Count) databases that will be joined to the Availability Group $AvailabilityGroup." foreach ($result in $testResult) { $server = $result.PrimaryServerSMO $ag = $result.AvailabilityGroupSMO $db = $result.DatabaseSMO $replicaServerSMO = $result.ReplicaServerSMO $restoreNeeded = $result.RestoreNeeded $backups = $result.Backups $replicaAgDbSMO = @{ } $targetSynchronizationState = @{ } $output = @( ) $progress['Activity'] = "Adding database $($db.Name) to Availability Group $AvailabilityGroup." $progress['Status'] = "Step 1/5: Setting seeding mode if needed." Write-Message -Level Verbose -Message $progress['Status'] Write-Progress @progress if ($SeedingMode) { Write-Message -Level Verbose -Message "Setting seeding mode to $SeedingMode." $failure = $false foreach ($replicaName in $replicaServerSMO.Keys) { $replica = $ag.AvailabilityReplicas[$replicaName] if ($replica.SeedingMode -ne $SeedingMode) { if ($Pscmdlet.ShouldProcess($server, "Setting seeding mode for replica $replica to $SeedingMode")) { try { Write-Message -Level Verbose -Message "Setting seeding mode for replica $replica to $SeedingMode." $replica.SeedingMode = $SeedingMode $replica.Alter() if ($SeedingMode -eq 'Automatic') { Write-Message -Level Verbose -Message "Setting GrantAvailabilityGroupCreateDatabasePrivilege on server $($replicaServerSMO[$replicaName]) for Availability Group $AvailabilityGroup." $replicaServerSMO[$replicaName].GrantAvailabilityGroupCreateDatabasePrivilege($AvailabilityGroup) $replicaServerSMO[$replicaName].Alter() } } catch { $failure = $true Stop-Function -Message "Failed setting seeding mode for replica $replica to $SeedingMode." -ErrorRecord $_ -Continue } } } } if ($failure) { Stop-Function -Message "Failed setting seeding mode to $SeedingMode." -Continue } } $progress['Status'] = "Step 2/5: Running backup and restore if needed." Write-Message -Level Verbose -Message $progress['Status'] Write-Progress @progress if ($restoreNeeded.Count -gt 0) { if (-not $backups) { if ($Pscmdlet.ShouldProcess($server, "Taking full and log backup of database $($db.Name)")) { try { Write-Message -Level Verbose -Message "Taking full and log backup of database $($db.Name)." $fullbackup = $db | Backup-DbaDatabase -BackupDirectory $SharedPath -Type Full -EnableException $logbackup = $db | Backup-DbaDatabase -BackupDirectory $SharedPath -Type Log -EnableException $backups = $fullbackup, $logbackup } catch { Stop-Function -Message "Failed to take full and log backup of database $($db.Name)." -ErrorRecord $_ -Continue } } } $failure = $false foreach ($replicaName in $restoreNeeded.Keys) { if ($Pscmdlet.ShouldProcess($replicaServerSMO[$replicaName], "Restore database $($db.Name) to replica $replicaName")) { try { Write-Message -Level Verbose -Message "Restore database $($db.Name) to replica $replicaName." $null = $backups | Restore-DbaDatabase -SqlInstance $replicaServerSMO[$replicaName] -NoRecovery -TrustDbBackupHistory -EnableException } catch { $failure = $true Stop-Function -Message "Failed to restore database $($db.Name) to replica $replicaName." -ErrorRecord $_ -Continue } } } if ($failure) { Stop-Function -Message "Failed to restore database $($db.Name)." -Continue } } $progress['Status'] = "Step 3/5: Add the database to the Availability Group on the primary replica." Write-Message -Level Verbose -Message $progress['Status'] if ($Pscmdlet.ShouldProcess($server, "Add database $($db.Name) to Availability Group $AvailabilityGroup on the primary replica")) { try { $progress['CurrentOperation'] = "State of AvailabilityDatabase for $($db.Name) on is not yet known." Write-Message -Level Verbose -Message "Object of type AvailabilityDatabase for $($db.Name) will be created. $($progress['CurrentOperation'])" Write-Progress @progress if ($ag.AvailabilityDatabases.Name -contains $db.Name) { Write-Message -Level Verbose -Message "Database $($db.Name) is already joined to Availability Group $AvailabilityGroup. No action will be taken on the primary replica." } else { $agDb = Get-DbaAgDatabase -SqlInstance $server -AvailabilityGroup $ag.Name -Database $db.Name $agDb = New-Object Microsoft.SqlServer.Management.Smo.AvailabilityDatabase($ag, $db.Name) $progress['CurrentOperation'] = "State of AvailabilityDatabase for $($db.Name) is $($agDb.State)." Write-Message -Level Verbose -Message "Object of type AvailabilityDatabase for $($db.Name) is created. $($progress['CurrentOperation'])" Write-Progress @progress $agDb.Create() $progress['CurrentOperation'] = "State of AvailabilityDatabase for $($db.Name) is $($agDb.State)." Write-Message -Level Verbose -Message "Method Create of AvailabilityDatabase for $($db.Name) is executed. $($progress['CurrentOperation'])" Write-Progress @progress # Wait for state to become Existing # https://docs.microsoft.com/en-us/dotnet/api/microsoft.sqlserver.management.smo.sqlsmostate $timeout = (Get-Date).AddSeconds($timeoutExisting) while ($agDb.State -ne 'Existing') { $progress['CurrentOperation'] = "State of AvailabilityDatabase for $($db.Name) is $($agDb.State), waiting for Existing." Write-Message -Level Verbose -Message $progress['CurrentOperation'] Write-Progress @progress if ((Get-Date) -gt $timeout) { Stop-Function -Message "Failed to add database $($db.Name) to Availability Group $AvailabilityGroup. Timeout of $timeoutExisting seconds is reached. State of AvailabilityDatabase for $($db.Name) is still $($agDb.State)." -Continue } Start-Sleep -Milliseconds $waitWhile $agDb.Refresh() } # Get customized SMO for the output $output += Get-DbaAgDatabase -SqlInstance $server -AvailabilityGroup $AvailabilityGroup -Database $db.Name -EnableException } } catch { Stop-Function -Message "Failed to add database $($db.Name) to Availability Group $AvailabilityGroup" -ErrorRecord $_ -Continue } } $progress['Status'] = "Step 4/5: Add the database to the Availability Group on the secondary replicas." Write-Message -Level Verbose -Message $progress['Status'] $failure = $false foreach ($replicaName in $replicaServerSMO.Keys) { if ($Pscmdlet.ShouldProcess($replicaServerSMO[$replicaName], "Add database $($db.Name) to Availability Group $AvailabilityGroup on replica $replicaName")) { $progress['CurrentOperation'] = "State of AvailabilityDatabase for $($db.Name) on replica $replicaName is not yet known." Write-Message -Level Verbose -Message $progress['CurrentOperation'] Write-Progress @progress try { $replicaAgDb = Get-DbaAgDatabase -SqlInstance $replicaServerSMO[$replicaName] -AvailabilityGroup $AvailabilityGroup -Database $db.Name -EnableException } catch { $failure = $true Stop-Function -Message "Failed to get database $($db.Name) on replica $replicaName." -ErrorRecord $_ -Continue } if ($replicaAgDb.IsJoined) { Write-Message -Level Verbose -Message "Database $($db.Name) is already joined to Availability Group $AvailabilityGroup. No action will be taken on the replica $replicaName." $replicaAgDbSMO[$replicaName] = $replicaAgDb } else { # Save SMO in array for the output $output += $replicaAgDb # Save SMO in hashtable for further processing $replicaAgDbSMO[$replicaName] = $replicaAgDb # Save target targetSynchronizationState for further processing # https://docs.microsoft.com/en-us/dotnet/api/microsoft.sqlserver.management.smo.availabilityreplicaavailabilitymode # https://docs.microsoft.com/en-us/dotnet/api/microsoft.sqlserver.management.smo.availabilitydatabasesynchronizationstate $availabilityMode = $ag.AvailabilityReplicas[$replicaName].AvailabilityMode if ($availabilityMode -eq 'AsynchronousCommit') { $targetSynchronizationState[$replicaName] = 'Synchronizing' } elseif ($availabilityMode -eq 'SynchronousCommit') { $targetSynchronizationState[$replicaName] = 'Synchronized' } else { $failure = $true Stop-Function -Message "Unexpected value '$availabilityMode' for AvailabilityMode on replica $replicaName." -Continue } $progress['CurrentOperation'] = "State of AvailabilityDatabase for $($db.Name) on replica $replicaName is $($replicaAgDb.State)." Write-Message -Level Verbose -Message $progress['CurrentOperation'] Write-Progress @progress # https://docs.microsoft.com/en-us/dotnet/api/microsoft.sqlserver.management.smo.sqlsmostate $timeout = (Get-Date).AddSeconds($timeoutExisting) while ($replicaAgDb.State -ne 'Existing') { $progress['CurrentOperation'] = "State of AvailabilityDatabase for $($db.Name) on replica $replicaName is $($replicaAgDb.State), waiting for Existing." Write-Message -Level Verbose -Message $progress['CurrentOperation'] Write-Progress @progress if ((Get-Date) -gt $timeout) { Stop-Function -Message "Failed to add database $($db.Name) on replica $replicaName. Timeout of $timeoutExisting seconds is reached. State of AvailabilityDatabase for $db is still $($replicaAgDb.State)." -Continue } Start-Sleep -Milliseconds $waitWhile $replicaAgDb.Refresh() } # With automatic seeding, .JoinAvailablityGroup() is not needed, just wait for the magic to happen if ($ag.AvailabilityReplicas[$replicaName].SeedingMode -ne 'Automatic') { try { $progress['CurrentOperation'] = "Joining database $($db.Name) on replica $replicaName." Write-Message -Level Verbose -Message $progress['CurrentOperation'] Write-Progress @progress $replicaAgDb.JoinAvailablityGroup() } catch { $failure = $true Stop-Function -Message "Failed to join database $($db.Name) on replica $replicaName." -ErrorRecord $_ -Continue } } } } } if ($failure) { Stop-Function -Message "Failed to add or join database $($db.Name)." -Continue } # Now we have configured everything and we only have to wait... $progress['Status'] = "Step 5/5: Wait for the database to finish joining the Availability Group on the secondary replicas." $progress['CurrentOperation'] = '' Write-Message -Level Verbose -Message $progress['Status'] Write-Progress @progress if ($Pscmdlet.ShouldProcess($server, "Wait for the database $($db.Name) to finish joining the Availability Group $AvailabilityGroup on the secondary replicas.")) { # We need to setup a progress bar for every replica to display them all at once. $syncProgressId = @{ } foreach ($replicaName in $replicaServerSMO.Keys) { $syncProgressId[$replicaName] = Get-Random } $stillWaiting = $true $timeout = (Get-Date).AddSeconds($timeoutSynchronization) while ($stillWaiting) { $stillWaiting = $false $failure = $false foreach ($replicaName in $replicaServerSMO.Keys) { if (-not $targetSynchronizationState[$replicaName]) { Write-Message -Level Verbose -Message "Database $($db.Name) is already joined to Availability Group $AvailabilityGroup. No action will be taken on the replica $replicaName." continue } if (-not $replicaAgDbSMO[$replicaName].IsJoined -or $replicaAgDbSMO[$replicaName].SynchronizationState -ne $targetSynchronizationState[$replicaName]) { $stillWaiting = $true } $syncProgress = @{ } $syncProgress['Id'] = $syncProgressId[$replicaName] $syncProgress['ParentId'] = $progress['Id'] $syncProgress['Activity'] = "Adding database(s) to Availability Group $AvailabilityGroup." if ($replicaAgDbSMO[$replicaName].SynchronizationState -ne $targetSynchronizationState[$replicaName]) { $syncProgress['Status'] = "IsJoined is $($replicaAgDbSMO[$replicaName].IsJoined), SynchronizationState is $($replicaAgDbSMO[$replicaName].SynchronizationState), waiting for $($targetSynchronizationState[$replicaName])." } else { $syncProgress['Status'] = "IsJoined is $($replicaAgDbSMO[$replicaName].IsJoined), SynchronizationState is $($replicaAgDbSMO[$replicaName].SynchronizationState), replica is in desired state." } if ($ag.AvailabilityReplicas[$replicaName].SeedingMode -eq 'Automatic' -and $reportSeeding) { $seedingStats = $server.Query("SELECT TOP 1 * FROM sys.dm_hadr_physical_seeding_stats WHERE local_database_name = '$($db.Name)' AND remote_machine_name = '$($ag.AvailabilityReplicas[$replicaName].EndpointUrl)' ORDER BY start_time_utc DESC") if ($seedingStats) { if ($seedingStats.failure_message -ne [DBNull]::Value) { $failure = $true Stop-Function -Message "Failed while seeding database $($db.Name) to $replicaName. failure_message: $($seedingStats.failure_message)." -Continue } $syncProgress['PercentComplete'] = [int]($seedingStats.transferred_size_bytes * 100.0 / $seedingStats.database_size_bytes) $syncProgress['SecondsRemaining'] = [int](($seedingStats.estimate_time_complete_utc - (Get-Date).ToUniversalTime()).TotalSeconds) $syncProgress['CurrentOperation'] = "Seeding state: $($seedingStats.internal_state_desc), $([int]($seedingStats.transferred_size_bytes/1024/1024)) out of $([int]($seedingStats.database_size_bytes/1024/1024)) MB transferred." } } Write-Message -Level Verbose -Message ($syncProgress['Status'] + $syncProgress['CurrentOperation']) Write-Progress @syncProgress } if ($failure) { $stillWaiting = $false Stop-Function -Message "Failed while seeding database $($db.Name)." -Continue } if ((Get-Date) -gt $timeout) { $stillWaiting = $false $failure = $true Stop-Function -Message "Failed to join or synchronize database $($db.Name). Timeout of $timeoutSynchronization seconds is reached. $progressOperation" -Continue } Start-Sleep -Milliseconds $waitWhile foreach ($replicaName in $replicaServerSMO.Keys) { $replicaAgDbSMO[$replicaName].Refresh() } } if ($failure) { Stop-Function -Message "Failed to join or synchronize database $($db.Name)." -Continue } } $output } } } # SIG # Begin signature block # MIIZewYJKoZIhvcNAQcCoIIZbDCCGWgCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUJWTCUcLSGT0J9c9RQOUHlru7 # 0+ugghSJMIIE/jCCA+agAwIBAgIQDUJK4L46iP9gQCHOFADw3TANBgkqhkiG9w0B # AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz # c3VyZWQgSUQgVGltZXN0YW1waW5nIENBMB4XDTIxMDEwMTAwMDAwMFoXDTMxMDEw # NjAwMDAwMFowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu # MSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMTCCASIwDQYJKoZIhvcN # AQEBBQADggEPADCCAQoCggEBAMLmYYRnxYr1DQikRcpja1HXOhFCvQp1dU2UtAxQ # tSYQ/h3Ib5FrDJbnGlxI70Tlv5thzRWRYlq4/2cLnGP9NmqB+in43Stwhd4CGPN4 # bbx9+cdtCT2+anaH6Yq9+IRdHnbJ5MZ2djpT0dHTWjaPxqPhLxs6t2HWc+xObTOK # fF1FLUuxUOZBOjdWhtyTI433UCXoZObd048vV7WHIOsOjizVI9r0TXhG4wODMSlK # XAwxikqMiMX3MFr5FK8VX2xDSQn9JiNT9o1j6BqrW7EdMMKbaYK02/xWVLwfoYer # vnpbCiAvSwnJlaeNsvrWY4tOpXIc7p96AXP4Gdb+DUmEvQECAwEAAaOCAbgwggG0 # MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsG # AQUFBwMIMEEGA1UdIAQ6MDgwNgYJYIZIAYb9bAcBMCkwJwYIKwYBBQUHAgEWG2h0 # dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAfBgNVHSMEGDAWgBT0tuEgHf4prtLk # YaWyoiWyyBc1bjAdBgNVHQ4EFgQUNkSGjqS6sGa+vCgtHUQ23eNqerwwcQYDVR0f # BGowaDAyoDCgLoYsaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJl # ZC10cy5jcmwwMqAwoC6GLGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFz # c3VyZWQtdHMuY3JsMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUHMAGGGGh0dHA6 # Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDovL2NhY2VydHMu # ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRFRpbWVzdGFtcGluZ0NB # LmNydDANBgkqhkiG9w0BAQsFAAOCAQEASBzctemaI7znGucgDo5nRv1CclF0CiNH # o6uS0iXEcFm+FKDlJ4GlTRQVGQd58NEEw4bZO73+RAJmTe1ppA/2uHDPYuj1UUp4 # eTZ6J7fz51Kfk6ftQ55757TdQSKJ+4eiRgNO/PT+t2R3Y18jUmmDgvoaU+2QzI2h # F3MN9PNlOXBL85zWenvaDLw9MtAby/Vh/HUIAHa8gQ74wOFcz8QRcucbZEnYIpp1 # FUL1LTI4gdr0YKK6tFL7XOBhJCVPst/JKahzQ1HavWPWH1ub9y4bTxMd90oNcX6X # t/Q/hOvB46NJofrOp79Wz7pZdmGJX36ntI5nePk2mOHLKNpbh6aKLzCCBRowggQC # oAMCAQICEAMFu4YhsKFjX7/erhIE520wDQYJKoZIhvcNAQELBQAwcjELMAkGA1UE # BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj # ZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUg # U2lnbmluZyBDQTAeFw0yMDA1MTIwMDAwMDBaFw0yMzA2MDgxMjAwMDBaMFcxCzAJ # BgNVBAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTEPMA0GA1UEBxMGVmllbm5hMREw # DwYDVQQKEwhkYmF0b29sczERMA8GA1UEAxMIZGJhdG9vbHMwggEiMA0GCSqGSIb3 # DQEBAQUAA4IBDwAwggEKAoIBAQC8v2N7q+O/vggBtpjmteofFo140k73JXQ5sOD6 # QLzjgija+scoYPxTmFSImnqtjfZFWmucAWsDiMVVro/6yGjsXmJJUA7oD5BlMdAK # fuiq4558YBOjjc0Bp3NbY5ZGujdCmsw9lqHRAVil6P1ZpAv3D/TyVVq6AjDsJY+x # rRL9iMc8YpD5tiAj+SsRSuT5qwPuW83ByRHqkaJ5YDJ/R82ZKh69AFNXoJ3xCJR+ # P7+pa8tbdSgRf25w4ZfYPy9InEvsnIRVZMeDjjuGvqr0/Mar73UI79z0NYW80yN/ # 7VzlrvV8RnniHWY2ib9ehZligp5aEqdV2/XFVPV4SKaJs8R9AgMBAAGjggHFMIIB # wTAfBgNVHSMEGDAWgBRaxLl7KgqjpepxA8Bg+S32ZXUOWDAdBgNVHQ4EFgQU8MCg # +7YDgENO+wnX3d96scvjniIwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsG # AQUFBwMDMHcGA1UdHwRwMG4wNaAzoDGGL2h0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNv # bS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMDWgM6Axhi9odHRwOi8vY3JsNC5kaWdp # Y2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDBMBgNVHSAERTBDMDcGCWCG # SAGG/WwDATAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20v # Q1BTMAgGBmeBDAEEATCBhAYIKwYBBQUHAQEEeDB2MCQGCCsGAQUFBzABhhhodHRw # Oi8vb2NzcC5kaWdpY2VydC5jb20wTgYIKwYBBQUHMAKGQmh0dHA6Ly9jYWNlcnRz # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJBc3N1cmVkSURDb2RlU2lnbmluZ0NB # LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQCPzflwlQwf1jak # EqymPOc0nBxiY7F4FwcmL7IrTLhub6Pjg4ZYfiC79Akz5aNlqO+TJ0kqglkfnOsc # jfKQzzDwcZthLVZl83igzCLnWMo8Zk/D2d4ZLY9esFwqPNvuuVDrHvgh7H6DJ/zP # Vm5EOK0sljT0UQ6HQEwtouH5S8nrqCGZ8jKM/+DeJlm+rCAGGf7TV85uqsAn5JqD # En/bXE1AlyG1Q5YiXFGS5Sf0qS4Nisw7vRrZ6Qc4NwBty4cAYjzDPDixorWI8+FV # OUWKMdL7tV8i393/XykwsccCstBCp7VnSZN+4vgzjEJQql5uQfysjcW9rrb/qixp # csPTKYRHMIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1b5VQCDANBgkqhkiG9w0B # AQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk # IElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgxMDIyMTIwMDAwWjByMQsw # CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu # ZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQg # Q29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA # +NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLXcep2nQUut4/6kkPApfmJ # 1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSRI5aQd4L5oYQjZhJUM1B0 # sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXiTWAYvqrEsq5wMWYzcT6s # cKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp6moKq4Tz # rGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8vYWxYoNzQYIH5DiLanMg # 0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYDVR0TAQH/BAgwBgEB/wIB # ADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMweQYIKwYBBQUH # AQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYI # KwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz # c3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0 # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaG # NGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD # QS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0 # dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYE # FFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6en # IZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPzItEVyCx8JSl2qB1dHC06 # GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRupY5a4l4kgU4QpO4/cY5j # DhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKNJK4kxscnKqEpKBo6cSgC # PC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7qPjFEmifz0DLQESlE/DmZAwlCEIy # sjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN3fYBIM6ZMWM9CBoYs4Gb # T8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKyZqHnGKSaZFHvMIIFMTCC # BBmgAwIBAgIQCqEl1tYyG35B5AXaNpfCFTANBgkqhkiG9w0BAQsFADBlMQswCQYD # VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGln # aWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew # HhcNMTYwMTA3MTIwMDAwWhcNMzEwMTA3MTIwMDAwWjByMQswCQYDVQQGEwJVUzEV # MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t # MTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5n # IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvdAy7kvNj3/dqbqC # mcU5VChXtiNKxA4HRTNREH3Q+X1NaH7ntqD0jbOI5Je/YyGQmL8TvFfTw+F+CNZq # FAA49y4eO+7MpvYyWf5fZT/gm+vjRkcGGlV+Cyd+wKL1oODeIj8O/36V+/OjuiI+ # GKwR5PCZA207hXwJ0+5dyJoLVOOoCXFr4M8iEA91z3FyTgqt30A6XLdR4aF5FMZN # JCMwXbzsPGBqrC8HzP3w6kfZiFBe/WZuVmEnKYmEUeaC50ZQ/ZQqLKfkdT66mA+E # f58xFNat1fJky3seBdCEGXIX8RcG7z3N1k3vBkL9olMqT4UdxB08r8/arBD13ays # 6Vb/kwIDAQABo4IBzjCCAcowHQYDVR0OBBYEFPS24SAd/imu0uRhpbKiJbLIFzVu # MB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMBIGA1UdEwEB/wQIMAYB # Af8CAQAwDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHkGCCsG # AQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29t # MEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNl # cnRBc3N1cmVkSURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8v # Y3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqg # OKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURS # b290Q0EuY3JsMFAGA1UdIARJMEcwOAYKYIZIAYb9bAACBDAqMCgGCCsGAQUFBwIB # FhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAsGCWCGSAGG/WwHATANBgkq # hkiG9w0BAQsFAAOCAQEAcZUS6VGHVmnN793afKpjerN4zwY3QITvS4S/ys8DAv3F # p8MOIEIsr3fzKx8MIVoqtwU0HWqumfgnoma/Capg33akOpMP+LLR2HwZYuhegiUe # xLoceywh4tZbLBQ1QwRostt1AuByx5jWPGTlH0gQGF+JOGFNYkYkh2OMkVIsrymJ # 5Xgf1gsUpYDXEkdws3XVk4WTfraSZ/tTYYmo9WuWwPRYaQ18yAGxuSh1t5ljhSKM # Ycp5lH5Z/IwP42+1ASa2bKXuh1Eh5Fhgm7oMLSttosR+u8QlK0cCCHxJrhO24XxC # QijGGFbPQTS2Zl22dHv1VjMiLyI2skuiSpXY9aaOUjGCBFwwggRYAgEBMIGGMHIx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJ # RCBDb2RlIFNpZ25pbmcgQ0ECEAMFu4YhsKFjX7/erhIE520wCQYFKw4DAhoFAKB4 # MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQB # gjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkE # MRYEFIPoBolz6G4E3mPxt+ZWbJWPlH2qMA0GCSqGSIb3DQEBAQUABIIBAGlJNgLu # nIh1P6QXackLr9OtfaxKWHLpNoLdkLF23VGznDulpTXritC1cLZEvEjzprPzf7vr # cT0v81LJUuNAlNLJPlJe+biIxQ+P/Dm3PoVAJ04yQGSlIbN2yDaRI6+GMU3i1h+P # P04x8klguRWcPN/mvm5CFE6l32E8NKgap9VVwCxp3IE2mElg3AyzqRL5hvxFSBCV # C4rCluKRzW1EAU5XsIvUg2Euem5WJna1nnCblfRNJteBDrl4OJIa/YqM7+ocH6Vm # aR8t7hq2F0FVOzJpIC+Bsf44dHJTddF3Ulw97arryjlcexV69+HeFOOolooVNhMN # Q8Akzx9N5++yNcKhggIwMIICLAYJKoZIhvcNAQkGMYICHTCCAhkCAQEwgYYwcjEL # MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 # LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElE # IFRpbWVzdGFtcGluZyBDQQIQDUJK4L46iP9gQCHOFADw3TANBglghkgBZQMEAgEF # AKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTIy # MDIwMjIyMDE1MlowLwYJKoZIhvcNAQkEMSIEIDC7DoQ0V1r6NiaK7j0r0Z+60XC8 # vwLGbLJE0qhJjZx0MA0GCSqGSIb3DQEBAQUABIIBAKWmupaXzmBvukXRIUUyVHyZ # 8iL++/lNz+4UlA0U4OL0irTe+MkmJDCFgu7g2yj1t6HrrVFCAm2jf+9ronH7N6hN # y654AIvxdb8ZfcOwytdlstF1/Mn1uRrRP7cniZac6DKPyg8RPuthdp/yL5uzaFAC # ESUPEtpOLi/p0l1kFLMvKFh2rZ+yMiI7vSKeqFfwf3dig7rLBZ13nVYtmlonFgys # yO2VkY696buS+gaTjYo95QTKxVJ7HPY0zmwpQD2HvdWa3Y47mc5t7Nw/OQRJ7/11 # VnXH/T0dfP9nFDjIuyJdlYUGzP1s1kz3CNTQgeAwsmVv5i2RBY99iq//HnyqCJs= # SIG # End signature block |