<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>SQLHenryW</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/sqlhenryweng/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/sqlhenryweng/feed.xml</link>
    <description>Premier Field Engineer for SQL Server and Microsoft R/ML</description>
    <lastBuildDate>Sat, 18 May 2019 20:02:10 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Transactional Replication Enhancement in SQL Server 2017 CU6 – Distribution database in AlwaysOn AG</title>
      <link>https://docs.microsoft.com/archive/blogs/sqlhenryweng/transactional-replication-enhancement-in-sql-server-2017-cu6-distribution-database-in-alwayson-ag</link>
      <pubDate>Thu, 24 May 2018 23:17:00 GMT</pubDate>
      <dc:creator><![CDATA[Henry Weng]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/sqlhenryweng/?p=405</guid>
      <description><![CDATA[&nbsp;

At the event of SQLSaturday Dallas 2018, it was a great honor to present this new feature...]]></description>
      <content:encoded><![CDATA[&nbsp;

At the event of SQLSaturday Dallas 2018, it was a great honor to present this new feature that allows you to put Distribution database in AlwaysOn AG. The slide deck for the session can be downloaded at <a href="https://www.sqlsaturday.com/SessionDownload.aspx?suid=20757" title="https://www.sqlsaturday.com/SessionDownload.aspx?suid=20757">https://www.sqlsaturday.com/SessionDownload.aspx?suid=20757</a>. As many audience requested, I am writing this blog to share the demo scripts, enjoy! <img class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://msdnshared.blob.core.windows.net/media/2018/05/wlEmoticon-smile5.png" />

<strong><u>Notes:</u></strong> The feature was released with SQL Server 2017 CU6 on 4/20/2018, <del datetime="2018-10-04T17:35:02+00:00">it will also be back ported to future CUs for SQL Server 2016 SP2</del> The feature now is also available in <a href="https://support.microsoft.com/en-us/help/4458871/cumulative-update-3-for-sql-server-2016-sp2">SQL Server 2016 SP2+CU3</a>.

In SQL 2017 + CU6, in order to support Distributor database in AG, there are a few highlights as below:
<ul>
 	<li>
<h5><span style="font-size: small">Remote Distributor required</span></h5>
</li>
 	<li>
<h5><span style="font-size: small">Distribution database AG needs a listener</span></h5>
</li>
 	<li>
<h5><span style="font-size: small">Replication jobs use the distributor listener</span></h5>
</li>
 	<li>
<h5><span style="font-size: small">A new job created to monitor distributor AG state</span></h5>
</li>
 	<li>
<h5><span style="font-size: small">Synchronous mode is recommended and preferred</span></h5>
</li>
 	<li>
<h5><span style="font-size: small">Transactional replication with immediate or queued updating subscriber is not supported</span></h5>
</li>
 	<li>
<h5><span style="font-size: small">P2P, Merge, Heterogeneous, Bi-directional TR not supported</span></h5>
</li>
 	<li>
<h5><span style="font-size: small">Configure by script only</span></h5>
</li>
 	<li>
<h5><span style="font-size: small">Must be new replication, existing distribution db not supported</span></h5>
</li>
</ul>
For the full list of supported/un-supported scenarios, please refer to - <a href="https://docs.microsoft.com/en-us/sql/relational-databases/replication/configure-distribution-availability-group?view=sql-server-2017">https://docs.microsoft.com/en-us/sql/relational-databases/replication/configure-distribution-availability-group?view=sql-server-2017</a>

This demo is to show you how to configure the Distribution database in AlwaysOn AG step by step, the demo environment:
<ul>
 	<li>
<h5><span style="font-size: small">SQL Server 2017 RTM + CU6 Build# 14.0.3025.34</span></h5>
</li>
 	<li>
<h5><span style="font-size: small">Distributor in AG (SQLNODE1 &amp; SQLNODE2)</span></h5>
</li>
 	<li>
<h5><span style="font-size: small">Publisher in AG (SQLNODE4 &amp; SQLNODE5)  - Test database AW2014 (it is a copy of AdventureWorks sample database, the table in the demo person.address_test is a copy of existing table person.address)</span></h5>
</li>
 	<li>
<h5><span style="font-size: small">Subscriber (SQLTest\SQL2017Sub)</span></h5>
</li>
</ul>
The goal in this demo is to make the transactional replication topology like this visually:

<a href="https://msdnshared.blob.core.windows.net/media/2018/05/image272.png"><img width="613" height="438" title="image" style="margin-right: auto;margin-left: auto;float: none" alt="image" src="https://msdnshared.blob.core.windows.net/media/2018/05/image_thumb205.png" border="0" /></a>

&nbsp;

Below is the scripts including comments that can help understand the whole process, BTW, the script is written in SQLCMD mode, so, make sure you set SSMS in SQLCMD mode before you play with the script in your testing/dev environment.

<strong><u><span>Important:</span></u></strong> All scripts/procedures provided here is for demo purpose only. Although it is tested in my lab, please read and test it thoroughly before you apply this to your system.
<blockquote><span style="font-size: small">
-- SECTION 1 ---- CONFIGURE THE DISTRIBUTOR SERVERS</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- Step1 - Configure the Distribution DB nodes (AG Replicas) to act as a distributor
:Connect SQLNode1
sp_adddistributor @distributor = @@ServerName, @password = 'Pass@word1'
Go
:Connect SQLNode2
sp_adddistributor @distributor = @@ServerName, @password = 'Pass@word1'
Go</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- Step2 - Configure the Distribution Database
:Connect SQLNode1
USE master
EXEC sp_adddistributiondb @database = 'DistributionDB', @security_mode = 1;
GO
Alter Database [DistributionDB] Set Recovery Full
Go
Backup Database [DistributionDB] to Disk = 'Nul'
Go</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- Step 3 - Create AG for the Distribution DB.
:Connect SQLNode1
USE [master]
GO
CREATE ENDPOINT [Hadr_endpoint]
STATE=STARTED
AS TCP (LISTENER_PORT = 5022, LISTENER_IP = ALL)
FOR DATA_MIRRORING (ROLE = ALL, AUTHENTICATION = WINDOWS NEGOTIATE
, ENCRYPTION = REQUIRED ALGORITHM AES)
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">:Connect SQLNode2
USE [master]
GO
CREATE ENDPOINT [Hadr_endpoint]
STATE=STARTED
AS TCP (LISTENER_PORT = 5022, LISTENER_IP = ALL)
FOR DATA_MIRRORING (ROLE = ALL, AUTHENTICATION = WINDOWS NEGOTIATE
, ENCRYPTION = REQUIRED ALGORITHM AES)
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">:Connect SQLNode1
-- Create the Availability Group
CREATE AVAILABILITY GROUP [DistributionDB_AG]
FOR DATABASE [DistributionDB]
REPLICA ON 'SQLNode1'
WITH (ENDPOINT_URL = N'TCP://SQLNode1.Features2014DC.local:5022',
FAILOVER_MODE = AUTOMATIC,
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
BACKUP_PRIORITY = 50,
SECONDARY_ROLE(ALLOW_CONNECTIONS = ALL),
SEEDING_MODE = AUTOMATIC),
N'SQLNode2' WITH (ENDPOINT_URL = N'TCP://SQLNode2.Features2014DC.local:5022',
FAILOVER_MODE = AUTOMATIC,
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
BACKUP_PRIORITY = 50,
SECONDARY_ROLE(ALLOW_CONNECTIONS = ALL),
SEEDING_MODE = AUTOMATIC);
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
:Connect SQLNode2
ALTER AVAILABILITY GROUP [DistributionDB_AG] JOIN
GO
ALTER AVAILABILITY GROUP [DistributionDB_AG] GRANT CREATE ANY DATABASE
Go</span>

<span style="font-size: small"> </span>

<span style="font-size: small">--STEP4 - Create the Listener for the Availability Group. This is very important.
:Connect SQLNode1</span>

<span style="font-size: small"> </span>

<span style="font-size: small">USE [master]
GO
ALTER AVAILABILITY GROUP [DistributionDB_AG]
ADD LISTENER N'DistributionDBList' (
WITH IP
((N'10.10.3.50', N'255.0.0.0')) , PORT=1433);
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- STEP 5 - Enable SQLNode2 also as a Distributor
:CONNECT SQLNODE2
EXEC sp_adddistributiondb @database = 'DistributionDB', @security_mode = 1;
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small"> </span>

<span style="font-size: small">--STEP 6 - On all Distributor Nodes Configure the Publisher Details
-- ******@Publisher is the current primary replca of the publisher, we will set redirect publisher to Publisher's listener in the end******-------------
:CONNECT SQLNODE1
sp_helpdistpublisher
EXEC sp_addDistPublisher @publisher = 'SQLNode4', @distribution_db = 'DistributionDB',
@working_directory = '\\SQL2014DC\Dist_Work_Directory\'
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">:CONNECT SQLNODE2
--sp_helpdistpublisher
EXEC sp_addDistPublisher @publisher = 'SQLNode4', @distribution_db = 'DistributionDB',
@working_directory = '\\SQL2014DC\Dist_Work_Directory\'
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- ******@Publisher is the current secondary replca of the publisher, we will set redirect publisher to Publisher's listener in the end******-------------
-- ******because after failover, the secondary replica of the publisher can also be a publisher to use the distributionDB******
:CONNECT SQLNODE1
EXEC sp_addDistPublisher @publisher = 'SQLNode5', @distribution_db = 'DistributionDB',
@working_directory = '\\SQL2014DC\Dist_Work_Directory\'
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">:CONNECT SQLNODE2
EXEC sp_addDistPublisher @publisher = 'SQLNode5', @distribution_db = 'DistributionDB',
@working_directory = '\\SQL2014DC\Dist_Work_Directory\'
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">--sp_helpdistpublisher</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- SECTION 2 ---- CONFIGURE THE PUBLISHER SERVER ON ALL PUBLISHER NODES
:CONNECT SQLNODE4
--sp_helpdistributor
EXEC sp_addDistributor @distributor = 'DistributionDBList', -- Listener for the Distribution DB.
@password = 'Pass@word1'
Go</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
:CONNECT SQLNODE5
EXEC sp_addDistributor @distributor = 'DistributionDBList', -- Listener for the Distribution DB.
@password = 'Pass@word1'
Go</span>

<span style="font-size: small"> </span>

<span style="font-size: small">--sp_helpdistributor</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- On the Secondary replicas of the Distribution DB, add the Subscriber as a linked server.
:CONNECT SQLNODE2
EXEC master.dbo.sp_addlinkedserver @server = N'SQLTest\SQL2017Sub', @srvproduct=N'SQL Server'
:CONNECT SQLNODE2
EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'SQLTest\SQL2017Sub',@useself=N'True',@locallogin=NULL,@rmtuser=NULL,@rmtpassword=NULL </span>

<span style="font-size: small"> </span>

<span style="font-size: small">  /* For security reasons the linked server remote logins password is changed with ######## */
--EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'SQLNODE5',@useself=N'True',@locallogin=NULL,@rmtuser=NULL,@rmtpassword=NULL </span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- On the Secondary replicas of the Publisher DB, add the Subscriber as a linked server.
:CONNECT SQLNODE5
EXEC master.dbo.sp_addlinkedserver @server = N'SQLTest\SQL2017Sub', @srvproduct=N'SQL Server'
:CONNECT SQLNODE5
EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'SQLTest\SQL2017Sub',@useself=N'True',@locallogin=NULL,@rmtuser=NULL,@rmtpassword=NULL </span>

<span style="font-size: small"> </span>

&nbsp;

<span style="font-size: small"> </span>

<span style="font-size: small">-- SECTION 3 ---- CONFIGURE DISTRIBUTION DATABASE FULL/LOG BACKUP PLAN
<strong>
-- ***Make sure Distribution database has Full and T-log backup scheduled since it is in FULL recovery mode ***--</strong></span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- SECTION 4 ---- CONFIGURE THE PUBLICATION FOR THE PRIMARY REPLICA (JUST TREAT IT AS STANDALONE)</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- On Publisher's primary replca, enable the dataabse AW2014 for transactional replication
:CONNECT SQLNODE4
USE master;
GO
EXEC sys.sp_replicationdboption
@dbname = 'AW2014',
@optname = 'publish',  --this is for transactional replication
@value = 'true';  </span>

<span style="font-size: small"> </span>

&nbsp;

<span style="font-size: small"> </span>

<span style="font-size: small">-- On primary Publisher SQLNode4, create the publication using script that you previously saved as below
-- created publication AW2014_Pub
-- You can script out your existing publication, here is just an example script that only contains 4 articles
-- execute the script to create the publication on SQLNode4</span>

<span style="font-size: small">-- create publication AW2014_Pub on the primary replica SQLNode4</span>

<span style="font-size: small"> </span>

<span style="font-size: small">:CONNECT SQLNODE4</span>

<span style="font-size: small"> </span>

<span style="font-size: small">--use [AW2014]
--exec sp_replicationdboption @dbname = N'AW2014', @optname = N'publish', @value = N'true'
--GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- Adding the logreader job
--@publisher_security_mode = 1: integrate authentication mode, no @publisher_login/password needed
--@publisher_security_mode = 0: SQL authentication mode, requires sql login for @publisher_login/password
use [AW2014]
exec [AW2014].sys.sp_addlogreader_agent @job_login = null, @job_password = null, @publisher_security_mode = 1, @publisher_login = null, @publisher_password = null
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- Adding the transactional publication
use [AW2014]
exec sp_addpublication @publication = N'AW2014_Pub', @description = N'Transactional publication of database ''AW2014'' from Publisher ''SQLNODE4''.', @sync_method = N'concurrent', @retention = 0, @allow_push = N'true', @allow_pull = N'true', @allow_anonymous = N'false', @enabled_for_internet = N'false', @snapshot_in_defaultfolder = N'true', @compress_snapshot = N'false', @ftp_port = 21, @allow_subscription_copy = N'false', @add_to_active_directory = N'false', @repl_freq = N'continuous', @status = N'active', @independent_agent = N'true', @immediate_sync = N'false', @allow_sync_tran = N'false', @allow_queued_tran = N'false', @allow_dts = N'false', @replicate_ddl = 1, @allow_initialize_from_backup = N'false', @enabled_for_p2p = N'false', @enabled_for_het_sub = N'false'
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- no need snapshot agent job
-- exec sp_addpublication_snapshot @publication = N'AW2014_Pub', @frequency_type = 1, @frequency_interval = 1, @frequency_relative_interval = 1, @frequency_recurrence_factor = 0, @frequency_subday = 8, @frequency_subday_interval = 1, @active_start_time_of_day = 0, @active_end_time_of_day = 235959, @active_start_date = 0, @active_end_date = 0, @job_login = null, @job_password = null, @publisher_security_mode = 1</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
use [AW2014]
exec sp_addarticle @publication = N'AW2014_Pub', @article = N'Address', @source_owner = N'Person', @source_object = N'Address', @type = N'logbased', @description = null, @creation_script = null, @pre_creation_cmd = N'drop', @schema_option = 0x000000000803509F, @identityrangemanagementoption = N'manual', @destination_table = N'Address', @destination_owner = N'Person', @vertical_partition = N'false', @ins_cmd = N'CALL sp_MSins_PersonAddress', @del_cmd = N'CALL sp_MSdel_PersonAddress', @upd_cmd = N'SCALL sp_MSupd_PersonAddress'
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">use [AW2014]
exec sp_addarticle @publication = N'AW2014_Pub', @article = N'Address_Test', @source_owner = N'Person', @source_object = N'Address_Test', @type = N'logbased', @description = null, @creation_script = null, @pre_creation_cmd = N'drop', @schema_option = 0x000000000803509F, @identityrangemanagementoption = N'manual', @destination_table = N'Address_Test', @destination_owner = N'Person', @vertical_partition = N'false', @ins_cmd = N'CALL sp_MSins_PersonAddress_Test', @del_cmd = N'CALL sp_MSdel_PersonAddress_Test', @upd_cmd = N'SCALL sp_MSupd_PersonAddress_Test'
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">use [AW2014]
exec sp_addarticle @publication = N'AW2014_Pub', @article = N'AddressType', @source_owner = N'Person', @source_object = N'AddressType', @type = N'logbased', @description = null, @creation_script = null, @pre_creation_cmd = N'drop', @schema_option = 0x000000000803509F, @identityrangemanagementoption = N'manual', @destination_table = N'AddressType', @destination_owner = N'Person', @vertical_partition = N'false', @ins_cmd = N'CALL sp_MSins_PersonAddressType', @del_cmd = N'CALL sp_MSdel_PersonAddressType', @upd_cmd = N'SCALL sp_MSupd_PersonAddressType'
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">use [AW2014]
exec sp_addarticle @publication = N'AW2014_Pub', @article = N'AWBuildVersion', @source_owner = N'dbo', @source_object = N'AWBuildVersion', @type = N'logbased', @description = null, @creation_script = null, @pre_creation_cmd = N'drop', @schema_option = 0x000000000803509F, @identityrangemanagementoption = N'manual', @destination_table = N'AWBuildVersion', @destination_owner = N'dbo', @vertical_partition = N'false', @ins_cmd = N'CALL sp_MSins_dboAWBuildVersion', @del_cmd = N'CALL sp_MSdel_dboAWBuildVersion', @upd_cmd = N'SCALL sp_MSupd_dboAWBuildVersion'
GO
</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- SECTION 4.2 ---- CONTINUE TO CONFIGURE THE PUBLICATION
-- check the publication
:CONNECT SQLNODE4
use [AW2014]
EXEC sp_helppublication @publication = N'AW2014_Pub'</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- set the publication to allow_initialize_from_backup
:CONNECT SQLNODE4
use [AW2014]
EXEC sp_changepublication
@Publication = N'AW2014_Pub',
@property = N'allow_initialize_from_backup',
@value = True</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- Section 4.3 ---- INIT SUBSCRIBER DATABASE USING BACK/RESTORE
:CONNECT SQLNODE4
BACKUP DATABASE [AW2014] TO  DISK = N'C:\Backups\UserDB\AW2014-ForSubscriber.bak' WITH NOFORMAT, NOINIT,
NAME = N'AW2014-Full Database Backup', SKIP, NOREWIND, NOUNLOAD,  STATS = 10
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">:CONNECT SQLNODE4
BACKUP LOG [AW2014] TO  DISK = N'C:\Backups\UserDB\AW2014-ForSubscriber.trn' WITH NOFORMAT, NOINIT,
NAME = N'AW2014-T Log Backup', SKIP, NOREWIND, NOUNLOAD,  STATS = 10
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
:CONNECT SQLTest\SQL2017Sub
/*
Noted: if the SQLTest\SQL2017Sub is higher than the publisher, then backup/restore will work,
otherwise you cannot use backup/restore to init subscriber db.
*/
USE [master]
RESTORE DATABASE [AW2014_Repl] FROM  DISK = N'\\sqlnode4\c$\Backups\UserDB\AW2014-ForSubscriber.bak' WITH  FILE = 1,
MOVE N'AdventureWorks2014_Data' TO N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017Sub\MSSQL\DATA\AW2014_ReplOLTP_Data.mdf',
MOVE N'AdventureWorks2014_Log' TO N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017Sub\MSSQL\DATA\AW2014_ReplOLTP_Log.ldf',
NORECOVERY,  NOUNLOAD,  STATS = 5</span>

<span style="font-size: small"> </span>

<span style="font-size: small">RESTORE LOG [AW2014_Repl] FROM  DISK = N'\\sqlnode4\c$\Backups\UserDB\AW2014-ForSubscriber.trn'
WITH  FILE = 1,  NOUNLOAD,  STATS = 5
GO</span>

<span style="font-size: small"> </span>

&nbsp;

<span style="font-size: small"> </span>

<span style="font-size: small">-- SECTION 5 ---- CREATE THE SUBSCRIPTION USING THE SYNC_TYPE = 'INITIALIZE WITH BACKUP'
-- execute the script to create subscription on the publisher SQLNode4
-- Sometimes you may use @sync_type = N'replication support only'
:CONNECT SQLNODE4
use [AW2014]
exec sp_addsubscription @publication = N'AW2014_Pub', @subscriber = N'SQLTest\SQL2017Sub', @destination_db = N'AW2014_Repl',
@subscription_type = N'Push',
@sync_type = N'initialize with backup', --INITIALIZE WITH BACKUP
@article = N'all', @update_mode = N'read only', @subscriber_type = 0,
@backupdevicetype ='disk',  --REQUIRED
@backupdevicename = N'\\SQLNode4\C$\Backups\UserDB\AW2014-ForSubscriber.bak'  --REQUIRED</span>

<span style="font-size: small"> </span>

<span style="font-size: small">:CONNECT SQLNODE4
use [AW2014]
exec sp_addpushsubscription_agent @publication = N'AW2014_Pub', @subscriber = N'SQLTest\SQL2017Sub',
@subscriber_db = N'AW2014_Repl', @job_login = null, @job_password = null, @subscriber_security_mode = 1,
@frequency_type = 64, @frequency_interval = 0, @frequency_relative_interval = 0, @frequency_recurrence_factor = 0,
@frequency_subday = 0, @frequency_subday_interval = 0, @active_start_time_of_day = 0,
@active_end_time_of_day = 235959, @active_start_date = 20180212, @active_end_date = 99991231,
@enabled_for_syncmgr = N'False', @dts_package_location = N'Distributor'
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
----------------------------CONFIGURE DISTRIBUTOR IN AG COMPLETED------------------------------</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
----------STOP HERE AT THIS POINT, TEST DISTRIBUTOR IN AG AND STANDALONE PUBLISHER-------------</span>

<span style="font-size: small"> </span>

<span style="font-size: small">/*
Distributor in AG (SQLNode1 &amp; SQLNode2)
SQLNode4 is the standalone publisher
SQLTest\SQL2017Sub is the subscriber
*/
--update table
:Connect SQLNODE4
update AW2014.person.address_test set addressline1 = '1972 Napa Ct.'
where addressid = 1</span>

<span style="font-size: small"> </span>

<span style="font-size: small">--watch result in publisher and subscriber
:Connect SQLNODE4
select top 5 * from AW2014.person.address_test
Go </span>

<span style="font-size: small"> </span>

<span style="font-size: small">:Connect SQLTest\SQL2017Sub
select top 5 * from AW2014_Repl.person.address_test
Go</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- check if the transaction and command is replicated to the secondary distributor replica
:connect sqlnode1
use distributiondb
select * from [dbo].[MSrepl_transactions] with(nolock)
select * from [dbo].[MSrepl_commands] with(nolock)
--where xact_seqno = 0x0000005300001A08001C
:connect sqlnode2
use distributiondb
select * from [dbo].[MSrepl_transactions] with(nolock)
select * from [dbo].[MSrepl_commands] with(nolock)
--where xact_seqno = 0x0000005300001A08001C</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- Failover Distributor AG  and Test the above table update again
-- 1. Failover Ditributor AG
:Connect SQLNODE1
:Connect SQLNODE2
ALTER AVAILABILITY GROUP [DistributionDB_AG] FAILOVER;
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- 2. Watch jobs on Distributor are disabled and enabled on different replicas automatically as expected (may take a min)</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- 3. Test the above table update again and watch if result is all good
--update table
:Connect SQLNODE4
update AW2014.person.address_test set addressline1 = '1975 Napa Ct.'
where addressid = 1</span>

<span style="font-size: small"> </span>

<span style="font-size: small">--watch result in publisher and subscriber
:Connect SQLNODE4
select top 5 * from AW2014.person.address_test
Go </span>

<span style="font-size: small"> </span>

<span style="font-size: small">:Connect SQLTest\SQL2017Sub
select top 5 * from AW2014_Repl.person.address_test
Go</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- 4. Failover back to SQLNODE1 as primary Distributor replica
:Connect SQLNODE2
:Connect SQLNODE1
ALTER AVAILABILITY GROUP [DistributionDB_AG] FAILOVER;
GO
-- 5. Wait a min or two, the jobs should be refreshed again as expected</span>

<span style="font-size: small"> </span>

&nbsp;

<span style="font-size: small"> </span>

<span style="font-size: small">------------------TEST DISTRIBUTOR IN AG AND STANDALONE PUBLISHER COMPLETED-----------------------</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
---------------IF THE ABOVE TEST SUCCESSFUL, CONTINUE TO CONFIGURE PUBLISHER IN AG----------------</span>

<span style="font-size: small"> </span>

&nbsp;

<span style="font-size: small"> </span>

<span style="font-size: small">-- SECTION 6 ---- ***CONFIGURE THE PUBLISHER DB AG AND PUBLISHER LISTENER***
-- Backup publisher database AW2014 on SQLNode4 and restore it on Secondary on SQLNode5 (with no recovery)
:CONNECT SQLNODE4
BACKUP DATABASE [AW2014] TO  DISK = N'C:\Backups\UserDB\AW2014-ForAG.bak' WITH NOFORMAT, NOINIT,
NAME = N'AW2014-Full Database Backup', SKIP, NOREWIND, NOUNLOAD,  STATS = 10
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">:CONNECT SQLNODE4
BACKUP LOG [AW2014] TO  DISK = N'C:\Backups\UserDB\AW2014-ForAG.trn' WITH NOFORMAT, NOINIT,
NAME = N'AW2014-T Log Backup', SKIP, NOREWIND, NOUNLOAD,  STATS = 10
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">:CONNECT SQLNODE5
USE [master]
RESTORE DATABASE [AW2014] FROM  DISK = N'\\SQLNode4\C$\Backups\UserDB\AW2014-ForAG.bak' WITH  FILE = 1,  NORECOVERY,  NOUNLOAD,  STATS = 5
RESTORE LOG [AW2014] FROM  DISK = N'\\SQLNode4\C$\Backups\UserDB\AW2014-ForAG.trn' WITH  FILE = 1,  NORECOVERY,  NOUNLOAD,  STATS = 5
GO</span>

<span style="font-size: small"> </span>

&nbsp;

<span style="font-size: small"> </span>

<span style="font-size: small">-- Create publisher AG "AW2014Pub_AG" Using GUI (Join Only). I did not script out this on-purpose to show that this part can be done with GUI, you can surely use script to create publisher database AG. Keep in mind, most of the Distributor database in AG part must be done with script, GUI doesn’t support it yet.
-- 1. Primary replica: SQLNODE4  (readable)
-- 2. Secondary replica: SQLNODE5  (readable)
-- 3. Database: AW2014</span>

<span style="font-size: small"> </span>

&nbsp;

<span style="font-size: small"> </span>

<span style="font-size: small">-- Add publisher AG listener - AW2014PubAGList
:CONNECT SQLNODE4
USE [master]
GO
ALTER AVAILABILITY GROUP [AW2014Pub_AG]
ADD LISTENER N'AW2014PubAGList' (
WITH IP
((N'10.10.3.56', N'255.0.0.0')
)
, PORT=1433);
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- Check all secondary replcas have replication feature installed --
:CONNECT SQLNODE5
USE master;
GO
DECLARE @installed int;
EXEC @installed = sys.sp_MS_replication_installed;
SELECT @installed;
-- Return 1 is installed</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- SECTION 7 ---- ***CONFIGURE THE REDIRECT PUBLISHER***
-- on paimary distributor replicas SQLNode1, add publisher redirect by using publsher database ag listener AW2014PubDist
:CONNECT SQLNODE1
USE distributionDB;
GO
EXEC sys.sp_redirect_publisher
@original_publisher = 'SQLNode4',
@publisher_db = 'AW2014',
@redirected_publisher = 'AW2014PubAGList';  </span>

<span style="font-size: small"> </span>

<span style="font-size: small">
:CONNECT SQLNODE1
USE distributionDB;
GO
EXEC sys.sp_redirect_publisher
@original_publisher = 'SQLNode5',
@publisher_db = 'AW2014',
@redirected_publisher = 'AW2014PubAGList';  </span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- check and validate all publisher replicas (must be on primary distributor replica)
/*
Notes: sp_validate_replica_hosts_as_publishers is an extention of sp_validate_redirected_publisher.
All publisher's replica must be readable/accessible to get them verified,
Otherwise, the validation will fail with error msg 21899.
*/
:CONNECT SQLNODE1
USE distributionDB;
GO
DECLARE @redirected_publisher sysname;
EXEC sys.sp_validate_replica_hosts_as_publishers
@original_publisher = 'SQLNode4',
@publisher_db = 'AW2014',
@redirected_publisher = @redirected_publisher output;
select @redirected_publisher as redirect_publisher</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- alternatively, check and validate with sp_validate_redirected_publisher (must be on primary distributor replica)
:CONNECT SQLNODE1
USE distributionDB;
GO
DECLARE @redirected_publisher sysname;
EXEC sys.sp_validate_redirected_publisher
@original_publisher = 'SQLNode4',
@publisher_db = 'AW2014',
@redirected_publisher = @redirected_publisher output;
select @redirected_publisher as redirect_publisher</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- or maybe simply use query to check redirect directly, result looks good too
-- this check doesn't guarentee all secondaries are accessible, only check its meta data
:CONNECT SQLNODE1
select * from DistributionDB.[dbo].[MSredirected_publishers]
/*
-- result looks good as below, expalin the result below --</span>

<span style="font-size: small"> </span>

<span style="font-size: small">original_publisher    publisher_db    redirected_publisher
----------------------------------------------------------
SQLNode4            AW2014            AW2014PubAGList
SQLNode5            AW2014            AW2014PubAGList
*/</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- check the distributor and publisher relationship in distribution db, the result looks good too
:CONNECT SQLNODE1
sp_helpdistpublisher</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-------------------------------PUBLISHER IN AG CONFIGURATION COMPLETED----------------------------------------</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
---------------------------NOW, BOTH DISTRIBUTOR AND PUBLISHER ARE IN AG -------------------------------------</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
--------------------------------------NOW TEST THE WHOLE THING------------------------------------------------</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- Test the replication behavior with both Publisher and Distributor in AGs --</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- ***Test the above table update again and watch if result is all good*** --</span>

<span style="font-size: small"> </span>

<span style="font-size: small">--use listener to update primary publisher database
:Connect AW2014PubAGList
update AW2014.person.address_test set addressline1 = '1789 Napa Ct.'
where addressid = 1</span>

<span style="font-size: small"> </span>

<span style="font-size: small">--check primary publisher database result
:Connect AW2014PubAGList
select * from AW2014.person.address_test where addressid = 1
Go </span>

<span style="font-size: small"> </span>

<span style="font-size: small">--check secondary publisher database result
:Connect SQLNODE5
select * from AW2014.person.address_test where addressid = 1
Go </span>

<span style="font-size: small"> </span>

<span style="font-size: small">--check the subscriber database result
:Connect SQLTest\SQL2017Sub
select * from AW2014_Repl.person.address_test where addressid = 1
Go</span>

<span style="font-size: small"> </span>

&nbsp;

<span style="font-size: small"> </span>

<span style="font-size: small">-- ***Failover Distributor AG and Test the above table update again*** --</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- 1. Failover Ditributor AG
:Connect SQLNODE1
:Connect SQLNODE2
ALTER AVAILABILITY GROUP [DistributionDB_AG] FAILOVER;
GO
-- 2. Watch jobs on Distributor are disabled and enabled on different replicas automatically as expected (may take a min)
-- 3. Test the above table update again and watch if result is all good
:Connect AW2014PubAGList
--use listener to update primary publisher database
update AW2014.person.address_test set addressline1 = '1790 Napa Ct.'
where addressid = 1</span>

<span style="font-size: small"> </span>

<span style="font-size: small">--check primary publisher database result
:Connect AW2014PubAGList
select * from AW2014.person.address_test where addressid = 1
Go </span>

<span style="font-size: small"> </span>

<span style="font-size: small">--check secondary publisher database result
:Connect SQLNODE5
select * from AW2014.person.address_test where addressid = 1
Go </span>

<span style="font-size: small"> </span>

<span style="font-size: small">--check the subscriber database result
:Connect SQLTest\SQL2017Sub
select * from AW2014_Repl.person.address_test where addressid = 1
Go</span>

<span style="font-size: small"> </span>

&nbsp;

<span style="font-size: small"> </span>

<span style="font-size: small">-- ***Failover Publisher AG and Test the above table update again*** --</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- 1. Failover Publisher database AG
:Connect SQLNODE4
:Connect SQLNODE5
ALTER AVAILABILITY GROUP [AW2014Pub_AG] FAILOVER;
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- 2. Test the above table update again and watch if result is all good
:Connect AW2014PubAGList
--use listener to update primary publisher database
update AW2014.person.address_test set addressline1 = '1791 Napa Ct.'
where addressid = 1</span>

<span style="font-size: small"> </span>

<span style="font-size: small">--check primary publisher database result
:Connect AW2014PubAGList
select * from AW2014.person.address_test where addressid = 1
Go </span>

<span style="font-size: small"> </span>

<span style="font-size: small">--check new secondary publisher database result
:Connect SQLNODE4
select * from AW2014.person.address_test where addressid = 1
Go </span>

<span style="font-size: small"> </span>

<span style="font-size: small">--check the subscriber database result
--it may take a while, eventually you will see logreader parse new log and distrib send tx/cmd over to the subscriber
:Connect SQLTest\SQL2017Sub
select * from AW2014_Repl.person.address_test where addressid = 1
Go</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- Default behavior and how TF1448 works – refer to </span><a href="https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-traceon-trace-flags-transact-sql?view=sql-server-2017" title="https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-traceon-trace-flags-transact-sql?view=sql-server-2017"><span style="font-size: small">https://docs.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-traceon-trace-flags-transact-sql?view=sql-server-2017</span></a>

<span style="font-size: small"> </span>

&nbsp;

<span style="font-size: small"> </span>

<span style="font-size: small">-- Failover back to SQLNODE1 as primary Distributor replica
:Connect SQLNODE2
:Connect SQLNODE1
ALTER AVAILABILITY GROUP [DistributionDB_AG] FAILOVER;
GO
-- Wait a min or two, the jobs should be refreshed again as expected</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-- Failover back to SQLNODE4 as primary Publisher replica
:Connect SQLNODE5
:Connect SQLNODE4
ALTER AVAILABILITY GROUP [AW2014Pub_AG] FAILOVER;
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- ***Final test with both Distributor and Publisher database failover together*** --
--update table
:Connect AW2014PubAGList
update AW2014.person.address_test set addressline1 = '1789 Napa Ct.'
where addressid = 1</span>

<span style="font-size: small"> </span>

<span style="font-size: small">--check primary publisher database result
:Connect AW2014PubAGList
select * from AW2014.person.address_test where addressid = 1
Go </span>

<span style="font-size: small"> </span>

<span style="font-size: small">--check secondary publisher database result
:Connect SQLNODE5
select * from AW2014.person.address_test where addressid = 1
Go </span>

<span style="font-size: small"> </span>

<span style="font-size: small">--check the subscriber database result
--it may take a while, refresh and eventually the tx/cmd is transmitted over to the subscriber through logread/distrib
:Connect SQLTest\SQL2017Sub
select * from AW2014_Repl.person.address_test where addressid = 1
Go</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
------------------------------------END - CONFIGURATION TEST COMPLETED HERE - END-----------------------------------------------</span>

<span style="font-size: small"> </span>

<span style="font-size: small">-------------------------------------------------------------CLEAN-UP SCRIPT---------------------------------------------------------------</span>

<span style="font-size: small">
-- Removing distributor and publisher
-- This workflow is about removing publisher and distributor when distribution database is in AG.
-- Publisher workflow
-- 1) On publisher, drop all the subscriptions and publications for this publisher
-- 2) And then call "sp_dropdistributor".
:CONNECT SQLNODE4
sp_dropdistributor</span>

<span style="font-size: small"> </span>

<span style="font-size: small">:CONNECT SQLNODE5
sp_dropdistributor</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- Distributors cleanup workflow
-- 1) Assuming SQLNode1 is the current primary of distribution database AG. SQLNode2 is the secondary replica.
-- 2) On SQLNode2, call "sp_dropdistpublisher '[Publiser]'" with parameter @no_checks = 1
:CONNECT SQLNODE2
exec sp_dropdistpublisher 'SQLNODE4', @no_checks = 1
exec sp_dropdistpublisher 'SQLNODE5', @no_checks = 1</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
--3) On distributor primary replica, call "sp_dropdistpublisher '[Publiser]'".
:CONNECT SQLNODE1
exec sp_dropdistpublisher 'SQLNODE4'
exec sp_dropdistpublisher 'SQLNODE5'</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
--4)    Delete the Distributor AG.
:CONNECT SQLNODE1
USE [master]
GO
DROP AVAILABILITY GROUP [DistributionDB_AG];
GO</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- 5) On the distributor secondary replica, change the "distribution" database to read_write mode by restoring the database with recovery.
-- Call "sp_dropdistributiondb 'distribution'" with parameter @former_ag_secondary=1 to drop distribution database
-- and to retain the snapshot directory. This will remove all the dangling jobs on this replica.
:CONNECT SQLNODE2
RESTORE DATABASE DistributionDB WITH RECOVERY
EXEC sp_dropdistributiondb 'DistributionDB', @former_ag_secondary=1</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- 6) On the distributor primary replica, call "sp_dropdistributiondb 'distribution'" to drop distribution database.
:CONNECT SQLNODE1
EXEC sp_dropdistributiondb 'DistributionDB'</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- 7) If there are no other distribution databases in AG, sp_dropdistributor can be done on all distributor replicas.
:CONNECT SQLNODE1
EXEC sp_dropdistributor
go</span>

<span style="font-size: small"> </span>

<span style="font-size: small">:CONNECT SQLNODE2
EXEC sp_dropdistributor</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- CHECK AND DELETE THE FOLLOWINGS MANUALLY
-- Any Replication SQL Agent jobs left on all servers
-- Any Replication related linked servers on all servers
-- Any Mirroring Endpoints for Replication AlwaysOn AG that is not in used
-- Drop any databases if it is part of setup
-- Drop any database backup files if it is part of setup</span>

<span style="font-size: small"> </span>

<span style="font-size: small">
-- Drop publisher AG
:CONNECT SQLNODE4
USE [master]
GO
DROP AVAILABILITY GROUP [AW2014Pub_AG];
GO
-- Drop any endpoints for publisher AG on SQLNode4 and SQLNode5
-- Drop the AW2014 database on SQLNode5 only (secondary)
-- Drop the AW2014_Repl database on subscriber SQLTest\SQL2017Sub
-- Delete all backup files for AG and Replication setup in \\sqlnode4\c$\Backups\UserDB\</span></blockquote>
&nbsp;

<strong>Special Thanks to our SQL Server Tiger team who made this happen!</strong>

Thanks,

<a>~Henry@MS</a>]]></content:encoded>
    </item>
    <item>
      <title>Configure TDE encrypted database in SQL Server AlwaysOn Availability Group</title>
      <link>https://docs.microsoft.com/archive/blogs/sqlhenryweng/configure-tde-encrypted-database-in-sql-server-alwayson-availability-group</link>
      <pubDate>Wed, 08 Nov 2017 20:56:02 GMT</pubDate>
      <dc:creator><![CDATA[Henry Weng]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/sqlhenryweng/?p=355</guid>
      <description><![CDATA[TDE has been out there since SQL Server 2008 and it is widely used to protect data/log/backup files...]]></description>
      <content:encoded><![CDATA[<p><br></p><p>TDE has been out there since SQL Server 2008 and it is widely used to protect data/log/backup files at rest. When I talked with DBAs sometimes, even with some very experienced DBAs, I still feel there is some confusing around the terms, such as Service Master Key (SMK), Database Master Key (DMK), Certificate and Database Encryption Key (DEK) etc… especially in the area how we backup, maintain those keys, when we should restore, re-create them. This topic is focusing on how to add a TDE encrypted database to AlwaysOn AG using one of the most commonly used TDE methods: SMK -&gt; DMK -&gt; Certificate -&gt; DEK -&gt; User Database. I added decent amount of comments in the script below, hopefully that can be helpful. <img class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://msdnshared.blob.core.windows.net/media/2017/11/wlEmoticon-smile2.png"><p>The following scripts are tested in my lab, feel free to test it in your testing environment.<p><strong><u>Lab Environment:</u></strong><ul><li>SQL Server 2016 AlwaysOn AG - 2 nodes:</li><li>Primary - SQL2016N1</li><li>Secondary - SQL2016N2</li><li>Assuming you already had OS Cluster configured and SQL installed. And AlwaysOn AG feature enabled in SQL Server configuration manager.</li></ul><p><strong><u>Basic knowledge:</u></strong><ol><li><strong>SMK</strong> - The SMK is automatically generated the first time the SQL Server instance is started and is used to encrypt a linked server password, credentials, and the database master key. The SMK is encrypted by using the local computer key using the Windows Data Protection API (DPAPI). The DPAPI uses a key that is derived from the Windows credentials of the SQL Server service account and the computer's credentials. The service master key can only be decrypted by the service account under which it was created or by a principal that has access to the machine's credentials. SMK is stored in master database.<li><strong>DMK</strong> - The database master key is a symmetric key that is used to protect the private keys of certificates and asymmetric keys that are present in the database. It can also be used to encrypt data, but it has length limitations that make it less practical for data than using a symmetric key. Noticed here, a DMK can be decrypted by SMK automatically or it can be decrypted by passwords using the OPEN MASTER KEY statement and an known/good password to open it explicitly, I said passwords, it is a plural, meaning a DMK can be decrypted by more than 1 passwords, so, if you don't know its original password when someone created it initially, don't worry, you can simply add a new password to it, as long as the SQL Server instance is running and the DMK can be opened by the SMK. A copy of DMK is stored in master database, and also it stored in the user database that has TDE enabled.<li><strong>Certificate</strong> - The certificate is encrypted by the DMK, you can consider that a certificate is equivalent to an asymmetric key.<li><strong>DEK</strong> - The DEK is encrypted by the certificate, and it is the actual key to encrypt the contents of the user database, the encrypted DEK is stored in the boot page of the user database, you can tell the boot page itself is not encrypted, right? Yes, that is correct. The boot page of a user database is not encrypted, the rest of pages are encrypted by the DEK, so, in order to access the rest of pages in the database, the DEK in the boot page must be decrypted first, which requires the certificate.</li></ol><p><strong><u></u></strong><strong><u>On the primary:</u></strong><strong><u></u></strong><p>
<pre><p>--create testing database
Create database DB_TDE_Test

--check if the DMK exist in master database
SELECT * FROM 
sys.symmetric_keys
WHERE name = '##MS_DatabaseMasterKey##'

--if not exist, create one
Use master
Go
CREATE MASTER KEY ENCRYPTION BY PASSWORD ='verycomplicateDMKpassword1234!!!'

--notice the DMK name is ##MS_DatabaseMasterKey## and its keyid is always 101
--if you just created on, this DMK is by default encrypted by the SMK and the password,
--that means you can decrypted it automatically when the SMK present on the server, </p><p>--or you can decrypted it by the password
SELECT * FROM 
sys.symmetric_keys
WHERE name = '##MS_DatabaseMasterKey##'

--now you need to create a certificate, which is encrypted by the DMK
CREATE CERTIFICATE TDE_Test_Cert WITH SUBJECT = 'TDE Certificate'

--check if the certificate is created in master database
--A certificate is equivalent to an asymmetric key
select * from sys.certificates

--now it is time to create DEK that is encrypted by the certificate
--DEK is stored in the user database
Use DB_TDE_Test

Create DATABASE ENCRYPTION KEY
WITH ALGORITHM = AES_256
ENCRYPTION BY SERVER CERTIFICATE TDE_Test_Cert

--you will see the warning message:
--Warning: The certificate used for encrypting the database encryption key has not been backed up. 
--You should immediately back up the certificate and the private key associated with the certificate. 
--If the certificate ever becomes unavailable or if you must restore or attach the database on another server, 
--you must have backups of both the certificate and the private key or you will not be able to open the database.

--backup SMK
Use master
Go
--backup SMK and keep it somewhere safe.
--backup SMK is always recommended, just in case when you server is crashed and you might need to restore it on new hardware.
--Restoring SMK is very rare, you don't need to have the same SMK on each nodes.
BACKUP SERVICE MASTER KEY&nbsp;&nbsp; TO FILE = 'c:\temp\service_master_key'&nbsp;&nbsp; ENCRYPTION BY PASSWORD = 'SMK_Backup_password123!!!';&nbsp; --backup DMK
--backup DMK and keep it somewhere safe.
--backup DMK is always recommended, it is similar as SMK, just in case, usually you don't need to restore it.
--Actually based on the DMK creation mechanism, even you restore it, it will be still a different DMK
--But we don't need to worry about that, because DMK is just used to encrypt the certificate, 
--as long as the DMK can be opened by the SMK, and the certificate can be decrypted and so the DEK can be decrypted,
--then our database can be accessed eventually. Confusing!!!! still?
Backup master key to file = 'c:\temp\database_master_key' encryption by password = 'DMK_Backup_password123!!!'


--backup certificate (***very important***)
--backup certificate is critical, make sure you always do so, the backup of the certificate will have a pair of files, one is the certificate, 
--the other one is the private key file that is encrypted by the given password.
--make sure you keep the password somewhere safe since it will be required when you need to create the same certificate on the other node using the backup file pair.
--if you forgot the certificate's backup password, you can simply back it up again using different password as long as the certificate can be opened by the DMK. 
BACKUP CERTIFICATE TDE_Test_Cert TO FILE = 'c:\temp\TDE_Test_Cert' 
WITH PRIVATE KEY ( 
					FILE = 'c:\temp\TDE_Test_Cert_Key' ,
					ENCRYPTION BY PASSWORD = 'TDE_Cert_Backup_password123!!!'
				 );


--enable TDE, now the database is TDE encrypted using the 
ALTER DATABASE DB_TDE_Test SET ENCRYPTION ON


SELECT * FROM sys.dm_database_encryption_keys;  
GO  

--check if the database is encrypted/progress (encryption_state = 3 encrypted; =2 in progress)
USE master 
GO 
SELECT db_name(database_id) [TDE Encrypted DB Name], c.name as CertName, encryptor_thumbprint , dek.*&nbsp;&nbsp;&nbsp;&nbsp; FROM sys.dm_database_encryption_keys dek&nbsp;&nbsp;&nbsp;&nbsp; INNER JOIN sys.certificates c on dek.encryptor_thumbprint = c.thumbprint

</p></pre>
<p><br><p><strong><u>On the secondary:</u></strong><p>
<pre>--copy all the backups from the primary node c:\temp\ to the secondary, that way, you don't need to change the file path in the code below
--or, you can modify the code below to reflect the path that matches your testing environment

Use master
Go

--check if DMK exists in master database on the secondary
--if yes, you don't need to create a new one, just use it
SELECT * FROM 
sys.symmetric_keys
WHERE name = '##MS_DatabaseMasterKey##'

--if the DMK doesn't exist in the master database on secondary, you can either restore the DMK from the backup from the primary, or you can create a new one
--fundamentally, based on the DMK generation mechanism the DMK will be different regardless of your restoring it or creating a new one.
--here I use restore on purpose, just wanted to show you what the each password means below:
--Decryption by password: this is the backup password that you used when you backup the DMK from primary, this will allow you to restore the DMK backup
--Encryption by password: this is a new password that will be used to encrypt/decrypt the DMK once it is restored, this password will allow you to open/decrypt the DMK
--Noticed the above two passwords are for different purpose.
Restore master key from file = 'c:\temp\database_master_key' 
Decryption by password ='DMK_Backup_password123!!!' --backup password
Encryption by password = 'verycomplicateDMKpassword1234!!!New'  --new DMK encryption password
/* optional
--or create a new DMK
Use master
Go
CREATE MASTER KEY ENCRYPTION BY PASSWORD ='verycomplicateDMKpassword1234!!!New'
*/

--Now, try to open the DMK with the new password, it should open successfully, now, you can alter it to be encrypted by SMK
OPEN MASTER KEY DECRYPTION BY PASSWORD = 'verycomplicateDMKpassword1234!!!New'

--Alter the DMK to be encrypted by the SMK
--The step is usually missed by some customers, what it does is to add the DMK to be encrypted by the SMK
--so that when the SQL Server service starts, the DMK will be automatically decrypted without explicitly opening it.
--this is required for TDE working properly
ALTER MASTER KEY ADD ENCRYPTION BY SERVICE MASTER KEY

--you can check if the DMK in the master database is now encrypted by the SMK (is_master_key_encrypted_by_server = 1)
select is_master_key_encrypted_by_server, * from sys.databases
where database_id = db_id(N'master')


--perfect! now, let's create certificate on the secondary using the backup pair that we made from the primary node earlier
--***Very important***, once you execute the below statement, the same certificate TDE_Test_Cert will be created from the backup pair, 
--noticed that it is now encrypted by the new DMK on the secondary node
CREATE CERTIFICATE TDE_Test_Cert FROM FILE = 'c:\temp\TDE_Test_Cert'
WITH PRIVATE KEY (FILE = 'c:\temp\TDE_Test_Cert_Key' ,
DECRYPTION BY PASSWORD = 'TDE_Cert_Backup_password123!!!');  --the backup password


--check the DMK and the certificate TDE_Test_Cert is created on the secondary
SELECT * FROM 
sys.symmetric_keys
WHERE name = '##MS_DatabaseMasterKey##'

select * from sys.certificates
</pre><p>
<strong><u>On the primary:</u></strong><p>
<pre>--Now it is ready to make a backup of the database [DB_TDE_Test] on the primary (SQL2016N1) and restore it to the secondary (SQL2016N2)

--Go to the primary SQL2016N1
BACKUP DATABASE [DB_TDE_Test] TO  DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\DB_TDE_Test_full.bak' 
WITH NOFORMAT, NOINIT,  NAME = N'DB_TDE_Test-Full Database Backup', 
SKIP, NOREWIND, NOUNLOAD,  STATS = 10
GO

BACKUP LOG [DB_TDE_Test] TO  DISK = N'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\DB_TDE_Test_tlog.trn' 
WITH NOFORMAT, NOINIT,  NAME = N'DB_TDE_Test-Full Database Backup', 
SKIP, NOREWIND, NOUNLOAD,  STATS = 10
GO

</pre><p><strong><u>On the secondary:</u></strong><p>
<pre>--Go to the secondary SQL2016N2, restore database with norecovery
--the restore should be successful, since we have all the keys/certificates ready on the secondary
USE [master]
RESTORE DATABASE [DB_TDE_Test] 
FROM  DISK = N'\\sql2016n1\C$\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\DB_TDE_Test_full.bak' 
WITH  FILE = 1,  NORECOVERY,  NOUNLOAD,  STATS = 5

--apply t-log with norecovery on the secondary
RESTORE LOG [DB_TDE_Test] 
FROM  DISK = N'\\sql2016n1\C$\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Backup\DB_TDE_Test_tlog.trn' 
WITH  FILE = 1,  NORECOVERY,  NOUNLOAD,  STATS = 5
</pre><p><strong>
Now, you can add the database to AG, you cannot use GUI, you have to use t-sql here</strong>
</p><p><strong><u>On the primary:</u></strong></p><p>
<pre>--add the database to a AG on the primary (SQL2016N1)
USE master 
go 
ALTER AVAILABILITY GROUP [AW2016AG] ADD DATABASE [DB_TDE_Test]
</pre>
<p><strong><u>On the secondary:</u></strong><p>
<pre>--go to the secondary (SQL2016N2), turn on HADR for the database
USE master 
go 
ALTER DATABASE [DB_TDE_Test] SET HADR AVAILABILITY GROUP = [AW2016AG];
</pre>
<p><strong>At this point, the database DB_TDE_Test is added to the AG successfully!</strong><p><strong><u>Now, let's clean it up after the test:</u></strong><p>
<pre>----clean up script after the test----

--on the primary replica (SQL2016N1)
use master;
go
ALTER AVAILABILITY GROUP [AW2016AG]
REMOVE DATABASE [DB_TDE_Test];
--drop the test database and its backup history
EXEC msdb.dbo.sp_delete_database_backuphistory @database_name = N'DB_TDE_Test'
GO
USE [master]
GO
ALTER DATABASE [DB_TDE_Test] SET  SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
USE [master]
GO
DROP DATABASE [DB_TDE_Test]
GO
--drop the certificate
DROP CERTIFICATE TDE_Test_Cert 
--drop the DMK
DROP MASTER KEY;
--check if they are cleaned up
select * from sys.certificates;
SELECT * FROM sys.symmetric_keys;

--on the secondary replica (SQL2016N2)
DROP DATABASE [DB_TDE_Test]
--drop the certificate
DROP CERTIFICATE TDE_Test_Cert 
--drop the DMK
DROP MASTER KEY;
--check if they are cleaned up
select * from sys.certificates;
SELECT * FROM sys.symmetric_keys;

</pre>
<p>Thanks for reading! If you have any questions, please feel free to post in the comments area and I will try my best to get back to you asap.]]></content:encoded>
    </item>
    <item>
      <title>How to setup Microsoft R Server One-box configuration to support AD account via LDAP</title>
      <link>https://docs.microsoft.com/archive/blogs/sqlhenryweng/how-to-setup-microsoft-r-server-one-box-configuration-to-support-ad-account-via-ldap</link>
      <pubDate>Fri, 03 Nov 2017 18:46:25 GMT</pubDate>
      <dc:creator><![CDATA[Henry Weng]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/sqlhenryweng/?p=345</guid>
      <description><![CDATA[One of my customers was asking how to setup Microsoft R Server, basically there are two...]]></description>
      <content:encoded><![CDATA[<p>One of my customers was asking how to setup Microsoft R Server, basically there are two options:<ol><li>One-box Configuration<li>Enterprise Configuration</li></ol><p>In this step-by-step POC tutorial, I will show you how to setup One-box configuration to support AD account via LDAP, it covers:<ol><li>Setup One-box configuration on Server side<li>Configure On-box configuration to support AD via LDAP<li>Setup Client side to connect to R Server and test it</li></ol><p><strong><u>One-box Configuration Lab Environment:</u></strong><ul><li>Microsoft R Server 9.1.0/SQLLite3.7 - SQL2016N2<li>RStudio + Microsoft R Client 3.4.1 - SQL2014Client<li>DC: SQL2014DC.Features2014DC.local<li>All same password for test only: Corp123!</li></ul><p><cite></cite><p><cite><strong><u>Steps tested in the Lab environment:</u></strong></cite><p><strong><font>R Server configuration (SQL2016N2):</font></strong><ol><li>Install R 9.1.0 in en_microsoft_r_server_910_for_windows_x64_10324119.zip (<cite>From &lt;<a href="https://docs.microsoft.com/en-us/machine-learning-server/install/r-server-install-windows">https://docs.microsoft.com/en-us/machine-learning-server/install/r-server-install-windows</a>&gt; )</cite></li><li>You can find the setup log (naming convention like this: Microsoft_R_Server_20171018143405.log) in %temp% to make sure installation are completed successfully</li><li>Connect and validate R Server installation locally</li></ol><ul><li>R Server runs on demand as a background process, as Microsoft R Engine in Task Manager. Server startup occurs when a client application like R Tools for Visual Studio or Rgui.exe connects to the server. </li><li>As a verification step, connect to the server and execute a few ScaleR functions to validate the installation. </li></ul><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1) Go to C:\Program Files\Microsoft\R Server\R_SERVER\bin\x64.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2) Double-click Rgui.exe to start the R Console application.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3) At the command line, type search() to show preloaded objects, including the RevoScaleR package. <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4) Type print(Revo.version) to show the software version.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5) Type rxSummary(~., iris) to return summary statistics on the built-in iris sample dataset. The rxSummary function is from&nbsp; RevoScaleR. </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.&nbsp; At this point, we know the R server installation are verified locally, let's configure the server, firstly, start Admin Utility - </p><p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CD C:\Program Files\Microsoft\R Server\R_SERVER\o16n\Microsoft.RServer.Utils.AdminUtil\</em><p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dotnet Microsoft.RServer.Utils.AdminUtil.dll</em><p><cite>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; From &lt;<a href="https://docs.microsoft.com/en-us/machine-learning-server/operationalize/configure-use-admin-utility">https://docs.microsoft.com/en-us/machine-learning-server/operationalize/configure-use-admin-utility</a>&gt; </cite><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong><u>Note:</u></strong> Local 'admin' account might be sufficient when trying to operationalize with a one-box configuration since everything is running within the trust boundary, it is insufficient for enterprise configurations.<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.&nbsp; Configure Server/Configure R Server for Operationalization</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A. One-box (web+compute nodes</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B. Set admin password: Corp123! ("admin" is the default admin account for One-box configuration, once you configure to use AD account, this admin account will not be used any more)</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6.&nbsp; Run Diagnostic Tests to test the configuration - <a href="https://docs.microsoft.com/en-us/machine-learning-server/operationalize/configure-run-diagnostics">https://docs.microsoft.com/en-us/machine-learning-server/operationalize/configure-run-diagnostics</a></p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A. Test configuration for a 'health report' of the configuration including a code execution test. Result:</p><p><a href="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image001.png"><img width="671" height="742" title="clip_image001" style="margin: 0px 0px 0px 58px" alt="clip_image001" src="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image001_thumb.png" border="0"></a><ul><li>Review the test results. If any issues arise, a raw report appears. You can also investigate the log files and attempt to resolve the issues.<li>After making your corrections, restart the component in question. It may take a few minutes for a component to restart.<li>Rerun the diagnostic test to make sure all is running smoothly now.</li></ul><p><strong><font>Client Configuration (SQL2014Client):</font></strong><p><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <u>Microsoft R Client overview:</u></strong><ul><li>R Client allows you to work with production data locally using the full set of ScaleR functions, but there are some constraints. On its own, the data to be processed must fit in local memory, and processing is capped at two threads for RevoScaleR functions. + <li>To benefit from disk scalability, performance and speed, push the compute context using rxSetComputeContext() to a production instance of Microsoft R Server (or R Server) such as <a href="https://msdn.microsoft.com/en-us/library/mt604845.aspx">SQL Server Machine Learning Services</a> and Machine Learning Server for Hadoop. <a href="https://docs.microsoft.com/en-us/machine-learning-server/r-client/compatibility-with-server">Learn more about its compatibility.</a>+ <li>You can offload heavy processing to Machine Learning Server or test your analytics during their developmentYou by running your code remotely using <a href="https://docs.microsoft.com/en-us/machine-learning-server/r/how-to-execute-code-remotely">remoteLogin() or remoteLoginAAD()</a> from the mrsdeploy package. </li></ul><p><cite>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; From &lt;<a href="https://docs.microsoft.com/en-us/machine-learning-server/r-client/install-on-windows">https://docs.microsoft.com/en-us/machine-learning-server/r-client/install-on-windows</a>&gt; </cite><p><strong>&nbsp;&nbsp;&nbsp;&nbsp; <u>Client Install:</u></strong><ol><li>Rstudio - <a href="https://www.rstudio.com/products/rstudio/download/">https://www.rstudio.com/products/rstudio/download/</a></li></ol><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Or Visual Studio 2015 + R Add-on - <a href="https://docs.microsoft.com/en-us/visualstudio/rtvs/installation">https://docs.microsoft.com/en-us/visualstudio/rtvs/installation</a><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.&nbsp; Download and install Microsoft R Client at <a href="https://aka.ms/rclient/">https://aka.ms/rclient/</a></p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.&nbsp; Taking Rstudio as example, set up Rstudio to use Microsoft R, go to <em>Tools -&gt; Global Options -&gt; General -&gt; R version</em> to point to the Microsoft R Client: [64-bit] C:\Program Files\Microsoft\R Client\R_SERVER</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Reference: <a href="https://support.rstudio.com/hc/en-us/articles/200486138-Using-Different-Versions-of-R">https://support.rstudio.com/hc/en-us/articles/200486138-Using-Different-Versions-of-R</a><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.&nbsp; Now you should be able to remotelogin the R Server SQL2016N2 and test it Test remote connection in R Studio from the client machine SQL2014Client:</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Refer to <a href="https://docs.microsoft.com/en-us/machine-learning-server/operationalize/how-to-connect-log-in-with-mrsdeploy#authentication">https://docs.microsoft.com/en-us/machine-learning-server/operationalize/how-to-connect-log-in-with-mrsdeploy#authentication</a><blockquote><p><font size="2">&gt; # EXAMPLE: LOGIN, CREATE REMOTE R SESSION, GO TO REMOTE PROMPT</font><p><font size="2">&gt; remoteLogin("https://SQL2016N2:12800") </font><p><font size="2"># here it will prompt you to enter the username: admin and its password you defined </font><p><font size="2">REMOTE&gt; x &lt;- 10 # Assign 10 to "x" in remote session</font><p><font size="2">REMOTE&gt; ls() # List objects in remote session </font><p><font size="2">[1] "x"</font><p><font size="2">REMOTE&gt; pause() # Pause remote interaction. Switch to local </font><p><font size="2">&gt; y &lt;- 10 # Assign 10 to "y" in local session </font><p><font size="2">&gt; ls() # List objects in local session </font><p><font size="2">[1] "y"</font><p><font size="2">&gt; putLocalObject(c("y")) # Loads local "y" into remote R session's workspace </font><p><font size="2">&gt; resume() # Resume remote interaction and move to remote command line </font><p><font size="2">REMOTE&gt; ls() # List the objects now in the remote session </font><p><font size="2">[1] "x" "y"</font><p><font size="2">REMOTE&gt; exit # Destroy remote session and logout </font><p><font size="2">&gt;</font></p></blockquote><p><strong><u>The below flow chart shows you how the client/server interacted with local and remote R Sessions using pause and resume:</u></strong><p><a href="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image0022.png"><img width="677" height="425" title="clip_image002" alt="clip_image002" src="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image002_thumb2.png" border="0"></a><p><cite>From &lt;<a href="https://docs.microsoft.com/en-us/machine-learning-server/operationalize/how-to-connect-log-in-with-mrsdeploy">https://docs.microsoft.com/en-us/machine-learning-server/operationalize/how-to-connect-log-in-with-mrsdeploy</a>&gt; </cite><p>Now, the client can remotelogin the R server and write R code, train a model, score a model and publish a model as a web service, it is all good, but we have give out the admin password to all R Contributors, for POC it may be ok, but in real world, we need this to support AD account, not just admin/password pair. We also need ability to categorize the permissions in different groups. Below will explain you how to do this. At high level, Microsoft R Server 9.1 supports three security roles:<ol><li><strong>Administrator/Owner</strong> - this role will be the owner of the R Server who have full control of the R server, can manage any service<li><strong>Contributor</strong> - this role will be the contributor of the R Server who can publish web services, such as R programmers, data scientists etc<li><strong>Reader</strong> - this role will be the reader of the R Server who can consumes the web services, such as application developers etc</li></ol><p>Now, let's continue to configure this One-box R Server to support AD account via LDAP.<ol><li>Firstly, in order to configure the R Server SQL2016N2 to use AD/LDAP, you need to add this role to the server:</li></ol><p><a href="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image003.png"><img width="770" height="285" title="clip_image003" style="margin: 0px 0px 0px 38px" alt="clip_image003" src="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image003_thumb.png" border="0"></a><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.&nbsp; After the feature/role installed, you need to setup it via the Setup Wizard below: More details in - <a href="https://blogs.msdn.microsoft.com/microsoftrservertigerteam/2017/04/10/step-by-step-guide-to-setup-ldaps-on-windows-server/">https://blogs.msdn.microsoft.com/microsoftrservertigerteam/2017/04/10/step-by-step-guide-to-setup-ldaps-on-windows-server/</a></p><p><a href="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image0045.png"><img width="545" height="426" title="clip_image004" style="margin: 0px 0px 0px 39px" alt="clip_image004" src="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image004_thumb5.png" border="0"></a><p><a href="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image005.png"><img width="548" height="428" title="clip_image005" style="margin: 0px 0px 0px 38px" alt="clip_image005" src="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image005_thumb.png" border="0"></a><ul><li>You don't have to setup SSL/TLS at this moment, because it will need appropriate certificate, I will put together another blog to configure LDAP-S, in this demo, it it just LDAP.</li></ul><p>&nbsp;&nbsp;&nbsp; 3.&nbsp; Now, back to AD, For demo purpose, I created the following Roles/Groups in AD (features2014dc.local) to use the R Server, you can refer to more details in <a href="https://blogs.msdn.microsoft.com/mlserver/2017/04/10/role-based-access-control-with-mrs-9-1-0/">https://blogs.msdn.microsoft.com/mlserver/2017/04/10/role-based-access-control-with-mrs-9-1-0/</a>, in this demo, I created below AD groups and users:</p><blockquote><p><font size="2">Group: <strong>MRSAdmins</strong> - this group will be the owner of the R Server who have full control of the R server</font><p><font size="2">Member: <strong>RAdmin1</strong></font><p><font size="2">Group: <strong>Rprogrammers</strong> - this group will be the contributor of the R Server who can publish web services</font><p><font size="2">Member: <strong>RProgrammer1</strong>, <strong>RProgrammer2</strong>, …</font><p><font size="2">Group: <strong>AppDevelopers</strong> - this group will be the reader of the R Server who consumes the web services</font><p><font size="2">Member: <strong>AppDev1</strong>, <strong>AppDev2</strong> …</font></p></blockquote><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <u>After you created the groups and demo users in AD, it will look like this:</u><p><a href="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image0065.png"><img width="855" height="447" title="clip_image006" style="margin: 0px 0px 0px 31px" alt="clip_image006" src="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image006_thumb4.png" border="0"></a><p>&nbsp;&nbsp; 4.&nbsp; Once this is completed, you need to setup R Server Role Configurations, you can reference to <a href="https://docs.microsoft.com/en-us/machine-learning-server/operationalize/configure-roles">https://docs.microsoft.com/en-us/machine-learning-server/operationalize/configure-roles</a> (this is for Enterprise Configuration, but it is similar), or just simply follow the below steps.</p><p>&nbsp;&nbsp; 5.&nbsp; Now we will need to make some changes in appsettings.json file for the web node, go to the folder (by default) C:\Program Files\Microsoft\R Server\R_SERVER\o16n\Microsoft.RServer.WebNode\</p><p><a href="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image007.png"><img width="839" height="244" title="clip_image007" style="margin: 0px 0px 0px 38px" alt="clip_image007" src="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image007_thumb.png" border="0"></a><p>&nbsp;&nbsp; 6.&nbsp; Find the file appsettings.json, make a backup (I made a backup file called appsettings-backup.json_for_admin as the above screenshot) of it before you making changes, just in case, so you don't need to re-configure previously completed steps, you can easily revert back to admin/password mode if you want to. Open the json file in notepad and find the section and make changes as below as the highlighted section according to your environment, I will explain it here:</p><ul><li><strong>Host</strong> - it is the IP of your DC<li><strong>QueryUserDn</strong> - is a domain account that has permission to query Active Directory using LDAP, I am thinking a service account will be preferred here, so you don't need to update this when a user leaves the company.<li><strong>QueryUserPassword</strong> - the domain account's password, which can be encrypted, if so, change to "QueryUserPasswordEncrypted": true<li><strong>SearchBase</strong> - can be the parent directory where you have the groups/users created in the above step 3, in this example, all users and groups created for R Server are under the CN=Users directory. <li>In the Authorization section, add the AD groups indicating the different R Server roles.</li></ul><blockquote><p><font size="2">"LDAP": {</font><p><font size="2">"Enabled": true,</font><p><font size="2">"Description": "Enable this section if you want to enable authentication via LDAP",</font><p><font size="2">"Host": "&lt;your_host_ip&gt;",</font><p><font size="2">"Port": 389,</font><p><font size="2">"UseLDAPS": false,</font><p><font size="2">"QueryUserDn": "CN=RDeployAmin,CN=Users,DC=FEATURES2014DC,DC=LOCAL",</font><p><font size="2">"QueryUserPassword": "P@$$w0rd!",</font><p><font size="2">"QueryUserPasswordEncrypted": false,</font><p><font size="2">"SearchBase": "CN=Users,DC=FEATURES2014DC,DC=LOCAL",</font><p><font size="2">"SearchFilter": "cn={0}",</font><p><font size="2">"UniqueUserIdentifierAttributeName": "userPrincipalName",</font><p><font size="2">"DisplayNameAttributeName": "name",</font><p><font size="2">"EmailAttributeName": "mail"</font><p><font size="2">},</font><p><font size="2">"Authorization": {</font><p><font size="2">"Owner": [ "MRSAdmins" ],</font><p><font size="2">"Contributor": [ "RProgrammers" ],</font><p><font size="2">"Reader": [ "App developers" ]</font><p><font size="2">},</font></p></blockquote><p>&nbsp; 7.&nbsp; Now, go back to Admin Utility, we will need to stop web node and start web node to make the change affected.</p><p><a href="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image0083.png"><img width="579" height="697" title="clip_image008" style="margin: 0px 0px 0px 37px" alt="clip_image008" src="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image008_thumb3.png" border="0"></a><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8.&nbsp; Now, we can do Diagnostic Test, since it is not using AD Authentication, the original Admin would be disabled automatically, so you will need to enter the AD account to validate it, for example, you can enter the AD account: RAdmin1/Corp123! - which is the owner role of the R Server, it should work.</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9.&nbsp; Local Diagnostic test passed. Great! Now, let’s test it from the Client machine SQL2014Client, in RStudio:</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong><u>Tested Owner - RAdmin1/Corp123! - it is in the MRSAdmins group in AD</u></strong><p><a href="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image0091.png"><img width="647" height="652" title="clip_image009" style="margin: 0px 0px 0px 38px" alt="clip_image009" src="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image009_thumb1.png" border="0"></a><p><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <u>Tested Contributor - RProgrammer1/Corp123! - it is in Rprogrammers group in AD</u></strong><p><a href="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image0104.png"><img width="625" height="749" title="clip_image010" style="margin: 0px 0px 0px 37px" alt="clip_image010" src="https://msdnshared.blob.core.windows.net/media/2017/11/clip_image010_thumb4.png" border="0"></a><p><br><p>Now you know how to make Microsoft R Server with One-box configuration to support AD account via LDAP. <p><strong><u>References:</u></strong><p>Role Based Access Control With MRS 9.1.0 - <a href="https://blogs.msdn.microsoft.com/mlserver/2017/04/10/role-based-access-control-with-mrs-9-1-0/">https://blogs.msdn.microsoft.com/mlserver/2017/04/10/role-based-access-control-with-mrs-9-1-0/</a><p>How to publish and manage R web services in Machine Learning Server with mrsdeploy - Owner/Contributors (Administrators or R Programmers) can publish R script as web service - <a href="https://docs.microsoft.com/en-us/machine-learning-server/operationalize/how-to-deploy-web-service-publish-manage-in-r">https://docs.microsoft.com/en-us/machine-learning-server/operationalize/how-to-deploy-web-service-publish-manage-in-r</a><p>App Developers - usually are the MSR Server Readers role, who can consume the service:<p><a href="https://docs.microsoft.com/en-us/machine-learning-server/operationalize/how-to-consume-web-service-interact-in-r">https://docs.microsoft.com/en-us/machine-learning-server/operationalize/how-to-consume-web-service-interact-in-r</a><p><a href="https://docs.microsoft.com/en-us/machine-learning-server/operationalize/how-to-consume-web-service-asynchronously-batch">https://docs.microsoft.com/en-us/machine-learning-server/operationalize/how-to-consume-web-service-asynchronously-batch</a><p>Thanks for reading, next, I will put up together a tutorial to configure One-box to support LDAP-S, which is LDAP over SSL/TLS.]]></content:encoded>
    </item>
    <item>
      <title>Using unbind to downgrade bound R Server 9.1.0</title>
      <link>https://docs.microsoft.com/archive/blogs/sqlhenryweng/using-unbind-to-downgrade-bound-r-server-9-1-0</link>
      <pubDate>Thu, 27 Jul 2017 15:38:31 GMT</pubDate>
      <dc:creator><![CDATA[Henry Weng]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/sqlhenryweng/?p=55</guid>
      <description><![CDATA[One of my customers recently had a situation that they upgraded their Microsoft R Server to the...]]></description>
      <content:encoded><![CDATA[<p><font size="2">One of my customers recently had a situation that they upgraded their Microsoft R Server to the latest version 9.1.0, and bound it to SQL Server 2016. Then they want to downgrade to SQL Server 2016 built-in MS R version 8.0.3. According to the document - </font><a href="https://docs.microsoft.com/en-us/sql/advanced-analytics/r/use-sqlbindr-exe-to-upgrade-an-instance-of-sql-server"><font size="2">https://docs.microsoft.com/en-us/sql/advanced-analytics/r/use-sqlbindr-exe-to-upgrade-an-instance-of-sql-server</font></a><font size="2">, you can surely upgrade it, but how do we downgrade. Here is what I tested in my lab step by step:<br><br></font><font size="2"><strong><u>Current environment:</u></strong> SQL Server 2016 SP1 CU3 + Microsoft R Server 9.1.0 (bound)<br></font><font size="2"><strong><u>Goal:</u></strong> downgrade R server 9.1.0 to lower version, which is 8.0.3 (the built-in MSR version in SQL Server 2016 SP1) in this test<br> </font><font size="2"></font>
<ol type="1"><font size="2"></font>
</ol><p><span><font size="2">1. Unbind the R Server 9.1.0 with the SQL Server instance</font></span>
</p><p><pre><code>cd “C:\Program Files\Microsoft\R Server\Setup”
sqlbindr.exe /list
sqlbindr.exe /unbind mssql13.mssqlserver
</code></pre><p>
</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/2017/07/image508.png"><img width="566" height="219" title="image" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/07/image_thumb423.png" border="0"></a><p>2. If I run the R validation script in SSMS, it failed as expected as the R Server is unbound with SQL Server instance.<p><a href="https://msdnshared.blob.core.windows.net/media/2017/07/image509.png"><img width="566" height="257" title="image" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/07/image_thumb424.png" border="0"></a><p>3. Since unbinding removes all versions of MRS on the instance, users need to run SQL Setup repair to restore MRS on the instance.<p><a href="https://msdnshared.blob.core.windows.net/media/2017/07/image511.png"><img width="569" height="221" title="image" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/07/image_thumb425.png" border="0"></a><p><a href="https://msdnshared.blob.core.windows.net/media/2017/07/image512.png"><img width="569" height="222" title="image" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/07/image_thumb426.png" border="0"></a><p>4. During the SQL repair, the SQL Server instance will be restarted, so it requires downtime. Once the repair completes, now you have the SQL Server 2016 SP1 build-in R 8.0.3 back.<p><a href="https://msdnshared.blob.core.windows.net/media/2017/07/image513.png"><img width="569" height="596" title="image" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/07/image_thumb427.png" border="0"></a><p>PG put a lot of effort to make the process easier, that is it! Hope this helps in case you are having similar issue.<br>Next time, I will put together a blog for how to in-place upgrade SQL Server 2012/2014 to SQL 2016 with R Services.</p><p>~heweng<br></p><ol type="1">
</ol>]]></content:encoded>
    </item>
  </channel>
</rss>