<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ryan Adams Data Blog</title>
	<atom:link href="https://www.ryanjadams.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.ryanjadams.com/</link>
	<description></description>
	<lastBuildDate>Thu, 17 Sep 2026 22:57:34 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/ryan-adams-site-icon-blue.png?fit=32%2C32&#038;ssl=1</url>
	<title>Ryan Adams Data Blog</title>
	<link>https://www.ryanjadams.com/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">69170873</site>	<item>
		<title>Use advanced SQL Server connection properties in Microsoft Fabric with the ODBC connector</title>
		<link>https://www.ryanjadams.com/2026/09/advanced-sql-server-connection-properties-fabric-odbc-connector/</link>
					<comments>https://www.ryanjadams.com/2026/09/advanced-sql-server-connection-properties-fabric-odbc-connector/#comments</comments>
		
		<dc:creator><![CDATA[RYANJADAMS]]></dc:creator>
		<pubDate>Thu, 17 Sep 2026 14:45:39 +0000</pubDate>
				<category><![CDATA[Data Factory]]></category>
		<category><![CDATA[Microsoft Fabric]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Always Encrypted]]></category>
		<category><![CDATA[Copy Job]]></category>
		<category><![CDATA[ODBC Connector]]></category>
		<category><![CDATA[On-premises Data Gateway]]></category>
		<guid isPermaLink="false">https://www.ryanjadams.com/?p=10073</guid>

					<description><![CDATA[<p>The built-in SQL Server connector in Fabric Data Factory doesn't expose connection string properties like ApplicationIntent, MultiSubnetFailover, or Column Encryption Setting. Here is a supported way around it: put those properties in an ODBC DSN on an on-premises data gateway and connect through the ODBC connector, demonstrated with Always Encrypted in a Copy job.</p>
<p>The post <a href="https://www.ryanjadams.com/2026/09/advanced-sql-server-connection-properties-fabric-odbc-connector/">Use advanced SQL Server connection properties in Microsoft Fabric with the ODBC connector</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Some SQL Server ingestion scenarios depend on connection-string properties that the built-in SQL Server connector in Data Factory for Microsoft Fabric doesn&#8217;t expose. If you need to route reads to an Availability Group secondary, connect reliably across a multi-subnet listener, or read columns protected by Always Encrypted, you&#8217;ve probably hit that wall. This post shows a supported way around it: use the ODBC connector together with an ODBC data source name (DSN) configured on an on-premises data gateway (OPDG). The example in this post uses Always Encrypted in a Copy job.</p>



<h2 class="wp-block-heading">Why these properties matter</h2>



<p class="wp-block-paragraph">Advanced SQL Server connection properties exist to support real enterprise patterns without sacrificing performance, availability, or security:</p>



<ul class="wp-block-list">
<li><code>ApplicationIntent=ReadOnly</code> directs the connection to a read-only secondary replica in an Availability Group, so your ingestion workload reads from a secondary node instead of the primary.</li>


<li><code>MultiSubnetFailover=true</code> tells the driver to attempt connections to the IP addresses returned by an Availability Group listener in parallel. When the Availability Group spans subnets, this delivers faster, more reliable connections during and after failover.</li>


<li><code>Column Encryption Setting=Enabled</code> allows the driver to transparently decrypt columns protected by Always Encrypted, where sensitive data stays encrypted both at rest and in transit and is decrypted only by an authorized client.</li>

</ul>



<p class="wp-block-paragraph">Each of these lives in the connection string. The challenge is getting Fabric to pass them through.</p>



<h2 class="wp-block-heading">The core idea: push the properties into an ODBC DSN</h2>



<p class="wp-block-paragraph">The solution is to move those properties into an ODBC DSN hosted on the gateway machine, and have Fabric connect through the ODBC connector. The DSN, backed by the Microsoft ODBC Driver for SQL Server, owns the connection string, including the advanced properties, and Fabric points at it by name.</p>



<p class="wp-block-paragraph">This example uses a Copy job, but the same ODBC connector and DSN approach applies to the Copy activity in a data pipeline.</p>



<p class="wp-block-paragraph">There&#8217;s one important identity consideration for Always Encrypted. Decrypting Always Encrypted columns requires the calling Windows identity to hold the column master key certificate in its personal (user) certificate store. By default the gateway&#8217;s Windows service runs as <code>NT SERVICE\PBIEgwService</code>, which authenticates as the machine&#8217;s computer account and has no user profile where that certificate can live. To make decryption work, the gateway must run under a service account, a domain user account, or a Group Managed Service Account (gMSA), whose user store contains the certificate.</p>



<h2 class="wp-block-heading">Walkthrough</h2>



<p class="wp-block-paragraph">Multiple technologies are used for the solution and links to the authoritative documentation for each technology are included. This post is intended to demonstrate how to put them together.</p>



<h3 class="wp-block-heading">1. Set up Always Encrypted on the source</h3>



<p class="wp-block-paragraph">Configure your column master key, column encryption key, and encrypted columns on SQL Server. To learn more, follow the <a href="https://learn.microsoft.com/sql/relational-databases/security/encryption/always-encrypted-tutorial-getting-started" target="_blank" rel="noopener nofollow noreferrer">Getting started with Always Encrypted</a> tutorial. As part of that setup, you export the column master key certificate, which you import on the gateway machine in a later step.</p>



<h3 class="wp-block-heading">2. Install and configure the on-premises data gateway</h3>



<p class="wp-block-paragraph">Stand up an OPDG on a machine that can reach your SQL Server. Refer to the <a href="https://learn.microsoft.com/data-integration/gateway/service-gateway-install" target="_blank" rel="noopener nofollow noreferrer">Install an on-premises data gateway</a> documentation.</p>



<h3 class="wp-block-heading">3. Create a service account</h3>



<p class="wp-block-paragraph">There are two choices for a service account. The first uses a dedicated Active Directory domain user account to run the gateway service, because a domain account has a real user profile and therefore a user certificate store. To create one, refer to <a href="https://learn.microsoft.com/windows-server/identity/ad-ds/get-started/adac/advanced-ad-ds-management-using-active-directory-administrative-center--level-200-" target="_blank" rel="noopener nofollow noreferrer">Manage user accounts with Active Directory Administrative Center</a>. Keep in mind that using a standard domain user account as a service account usually means setting its password to never expire, which is <strong>not recommended</strong>.</p>



<div class="wp-block-group has-background has-global-padding is-layout-constrained wp-container-core-group-is-layout-14085c7e wp-block-group-is-layout-constrained" style="border-radius:6px;border-left-color:#2e7d32;border-left-width:5px;background-color:#edf7ed;padding-top:1.1rem;padding-right:1.35rem;padding-bottom:1.1rem;padding-left:1.35rem">
<p class="has-text-color wp-block-paragraph" style="color:#1b5e20;font-size:0.82rem;letter-spacing:0.08em;text-transform:uppercase"><strong>Recommended</strong></p>



<p class="wp-block-paragraph">The second and recommended approach is a Group Managed Service Account (gMSA), which manages its password automatically. You can learn more in the documentation on <a href="https://learn.microsoft.com/windows-server/identity/ad-ds/manage/group-managed-service-accounts/group-managed-service-accounts/manage-group-managed-service-accounts?tabs=aduc" target="_blank" rel="noopener nofollow noreferrer">Getting started with Group Managed Service Accounts</a>.</p>
</div>



<h3 class="wp-block-heading">4. Change the gateway service account</h3>



<p class="wp-block-paragraph">Change the service account from the default <code>NT SERVICE\PBIEgwService</code> to the account you created. For a domain user account, use the on-premises data gateway app rather than the Windows Services console so the account receives all required privileges. For the steps, refer to <a href="https://learn.microsoft.com/data-integration/gateway/service-gateway-service-account" target="_blank" rel="noopener nofollow noreferrer">Change the on-premises data gateway service account</a>. You can&#8217;t set a gMSA through the gateway app, because you don&#8217;t know its password. Instead, stop the service, change the account, and restart it with PowerShell:</p>


<pre class="wp-block-code language-powershell"><span><code class="hljs language-powershell"><span class="hljs-built_in">Stop-Service</span> <span class="hljs-literal">-Name</span> PBIEgwService

<span class="hljs-variable">$svc</span> = <span class="hljs-built_in">Get-WmiObject</span> Win32_Service <span class="hljs-literal">-Filter</span> <span class="hljs-string">"Name='PBIEgwService'"</span>
<span class="hljs-variable">$svc</span>.Change(
    <span class="hljs-variable">$null</span>, <span class="hljs-variable">$null</span>, <span class="hljs-variable">$null</span>, <span class="hljs-variable">$null</span>, <span class="hljs-variable">$null</span>, <span class="hljs-variable">$null</span>,
    <span class="hljs-string">"CONTOSO\opdg-sql<span class="hljs-variable">$</span>"</span>, <span class="hljs-string">""</span>
)

<span class="hljs-built_in">Start-Service</span> <span class="hljs-literal">-Name</span> PBIEgwService</code></span></pre>


<h3 class="wp-block-heading">5. Grant the service account access in SQL Server</h3>



<p class="wp-block-paragraph">Give the account that runs the gateway service the SQL Server permissions it needs to read the source database and table. Use the same account you configured for the gateway: a domain user account appears as <code>CONTOSO\svc-opdg</code>, and a gMSA appears as <code>CONTOSO\opdg-sql$</code> with a trailing <code>$</code>.</p>



<h3 class="wp-block-heading">6. Import the certificate into the service account&#8217;s user store</h3>



<p class="wp-block-paragraph">If you use a domain user account, sign in to the gateway machine as that account and import the certificate you exported earlier through Certificate Manager. A gMSA has no interactive sign-in, so you can&#8217;t log on as it to import the certificate. Instead, use <a href="https://live.sysinternals.com/" target="_blank" rel="noopener nofollow noreferrer">PsExec from Sysinternals</a> to open a command prompt in the gMSA&#8217;s context, and then start Certificate Manager from there. Run <code>psexec -i -u DOMAIN\gmsaAccount$ -p ~ cmd.exe</code>, and then run <code>certmgr.msc</code> in the new prompt to import the certificate. For a similar setup for SSRS using a gMSA, refer to the <a href="https://www.ryanjadams.com/2020/11/always-encrypted-data-displayed-in-ssrs-with-a-gmsa/">Always Encrypted data displayed in SSRS with a gMSA</a> post for another example.</p>



<h3 class="wp-block-heading">7. Create the DSN</h3>



<p class="wp-block-paragraph">On the gateway machine, create a System DSN using the ODBC Driver for SQL Server. Give the DSN a name and point it at your server, choose Integrated Windows authentication, enable Column Encryption, and, for Availability Group scenarios, set Application intent to READONLY and select Multi-subnet failover.</p>



<figure class="wp-block-image size-large" style="max-width:464px;margin-left:0;text-align:left"><img data-recalc-dims="1" decoding="async" width="464" height="352" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/odbc-driver-selection.png?resize=464%2C352&#038;ssl=1" alt="Selecting the ODBC Driver for SQL Server in the Create New Data Source wizard." class="wp-image-10065" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/odbc-driver-selection.png?w=464&amp;ssl=1 464w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/odbc-driver-selection.png?resize=300%2C228&amp;ssl=1 300w" sizes="auto, (max-width: 464px) 100vw, 464px" /><figcaption class="wp-block-image__caption" style="text-align:left">Selecting the ODBC Driver for SQL Server in the Create New Data Source wizard.</figcaption></figure>



<figure class="wp-block-image size-large" style="max-width:566px;margin-left:0;text-align:left"><img data-recalc-dims="1" decoding="async" width="566" height="463" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/odbc-dsn-name-and-server.png?resize=566%2C463&#038;ssl=1" alt="Naming the DSN and specifying the SQL Server to connect to." class="wp-image-10066" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/odbc-dsn-name-and-server.png?w=566&amp;ssl=1 566w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/odbc-dsn-name-and-server.png?resize=300%2C245&amp;ssl=1 300w" sizes="auto, (max-width: 566px) 100vw, 566px" /><figcaption class="wp-block-image__caption" style="text-align:left">Naming the DSN and specifying the SQL Server to connect to.</figcaption></figure>



<figure class="wp-block-image size-large" style="max-width:566px;margin-left:0;text-align:left"><img data-recalc-dims="1" decoding="async" width="566" height="463" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/odbc-dsn-windows-authentication.png?resize=566%2C463&#038;ssl=1" alt="Choosing Integrated Windows authentication for the DSN." class="wp-image-10067" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/odbc-dsn-windows-authentication.png?w=566&amp;ssl=1 566w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/odbc-dsn-windows-authentication.png?resize=300%2C245&amp;ssl=1 300w" sizes="auto, (max-width: 566px) 100vw, 566px" /><figcaption class="wp-block-image__caption" style="text-align:left">Choosing Integrated Windows authentication for the DSN.</figcaption></figure>



<figure class="wp-block-image size-large" style="max-width:566px;margin-left:0;text-align:left"><img data-recalc-dims="1" decoding="async" width="566" height="463" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/odbc-dsn-advanced-properties.png?resize=566%2C463&#038;ssl=1" alt="The three advanced properties in the DSN: Application intent set to READONLY, Multi-subnet failover, and Column Encryption enabled." class="wp-image-10068" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/odbc-dsn-advanced-properties.png?w=566&amp;ssl=1 566w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/odbc-dsn-advanced-properties.png?resize=300%2C245&amp;ssl=1 300w" sizes="auto, (max-width: 566px) 100vw, 566px" /><figcaption class="wp-block-image__caption" style="text-align:left">The three advanced properties in the DSN: Application intent set to READONLY, Multi-subnet failover, and Column Encryption enabled.</figcaption></figure>



<h3 class="wp-block-heading">8. Create a Copy job using the ODBC connector</h3>



<p class="wp-block-paragraph">In your Copy job, choose the ODBC connector and reference the DSN in the ODBC connection string, for example <code>dsn=opsql</code>. Select your OPDG as the data gateway. You can leave Authentication kind set to Anonymous, because the ODBC driver named in the DSN handles authentication to SQL Server and overrides Fabric&#8217;s authentication kind.</p>



<figure class="wp-block-image size-large" style="max-width:668px;margin-left:0;text-align:left"><img data-recalc-dims="1" decoding="async" width="668" height="524" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/fabric-odbc-connection-gateway.png?resize=668%2C524&#038;ssl=1" alt="Configuring the ODBC connection in Fabric with the DSN, the on-premises data gateway, and Anonymous authentication." class="wp-image-10069" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/fabric-odbc-connection-gateway.png?w=668&amp;ssl=1 668w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/fabric-odbc-connection-gateway.png?resize=300%2C235&amp;ssl=1 300w" sizes="auto, (max-width: 668px) 100vw, 668px" /><figcaption class="wp-block-image__caption" style="text-align:left">Configuring the ODBC connection in Fabric with the DSN, the on-premises data gateway, and Anonymous authentication.</figcaption></figure>



<h3 class="wp-block-heading">9. Verify decryption</h3>



<p class="wp-block-paragraph">Without <code>Column Encryption Setting=Enabled</code>, the encrypted <code>Last_Name</code> column comes across as ciphertext. The data is protected but unreadable:</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" decoding="async" width="1190" height="475" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/preview-encrypted-last-name.png?resize=1190%2C475&#038;ssl=1" alt="Preview of the source table without column encryption enabled, showing encrypted Last_Name values." class="wp-image-10070" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/preview-encrypted-last-name.png?w=1190&amp;ssl=1 1190w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/preview-encrypted-last-name.png?resize=300%2C120&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/preview-encrypted-last-name.png?resize=1024%2C409&amp;ssl=1 1024w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/preview-encrypted-last-name.png?resize=768%2C307&amp;ssl=1 768w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /><figcaption class="wp-block-image__caption">Preview of the source table without column encryption enabled, showing encrypted Last_Name values.</figcaption></figure>



<p class="wp-block-paragraph">With the setting enabled in the DSN and the certificate present in the service account&#8217;s user store, the <code>Last_Name</code> column is decrypted and lands in plaintext:</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" decoding="async" width="1194" height="628" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/preview-decrypted-last-name.png?resize=1194%2C628&#038;ssl=1" alt="Preview of the same table with column encryption enabled, showing decrypted Last_Name values." class="wp-image-10071" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/preview-decrypted-last-name.png?w=1194&amp;ssl=1 1194w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/preview-decrypted-last-name.png?resize=300%2C158&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/preview-decrypted-last-name.png?resize=1024%2C539&amp;ssl=1 1024w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/preview-decrypted-last-name.png?resize=768%2C404&amp;ssl=1 768w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /><figcaption class="wp-block-image__caption">Preview of the same table with column encryption enabled, showing decrypted Last_Name values.</figcaption></figure>



<h2 class="wp-block-heading">Boundaries and things to know</h2>



<ul class="wp-block-list">
<li>The ODBC connector requires an on-premises data gateway. In a Copy job it supports Full load and Append, as both source and destination.</li>


<li>The certificate must reside in the user store of the exact account running the gateway service.</li>


<li>The ODBC Driver for SQL Server must be installed on the gateway host, and the DSN must be a System DSN so the gateway service can see it.</li>

</ul>



<h2 class="wp-block-heading">Next steps</h2>



<p class="wp-block-paragraph">The ODBC connector with a DSN gives you a supported path for advanced SQL Server connection properties, including reading Always Encrypted data into Fabric. To learn more, explore the following documentation:</p>



<ul class="wp-block-list">
<li><a href="https://learn.microsoft.com/fabric/data-factory/connector-odbc-overview" target="_blank" rel="noopener nofollow noreferrer">ODBC connector overview</a></li>


<li><a href="https://learn.microsoft.com/fabric/data-factory/connector-odbc-copy-activity" target="_blank" rel="noopener nofollow noreferrer">Configure ODBC in a copy activity</a></li>

</ul>
<p>The post <a href="https://www.ryanjadams.com/2026/09/advanced-sql-server-connection-properties-fabric-odbc-connector/">Use advanced SQL Server connection properties in Microsoft Fabric with the ODBC connector</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ryanjadams.com/2026/09/advanced-sql-server-connection-properties-fabric-odbc-connector/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">10073</post-id>	</item>
		<item>
		<title>Manage Fabric connections at scale with connection recency in Fabric REST APIs</title>
		<link>https://www.ryanjadams.com/2026/09/manage-fabric-connections-at-scale-connection-recency/</link>
					<comments>https://www.ryanjadams.com/2026/09/manage-fabric-connections-at-scale-connection-recency/#comments</comments>
		
		<dc:creator><![CDATA[RYANJADAMS]]></dc:creator>
		<pubDate>Thu, 03 Sep 2026 15:00:00 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Microsoft Fabric]]></category>
		<category><![CDATA[Data Factory]]></category>
		<category><![CDATA[Governance]]></category>
		<category><![CDATA[REST API]]></category>
		<guid isPermaLink="false">https://www.ryanjadams.com/?p=10022</guid>

					<description><![CDATA[<p>Connections are shared infrastructure in Microsoft Fabric, and at enterprise scale it gets hard to tell which ones are still in use. Connection Recency in the Fabric REST APIs surfaces when a connection was last linked and last used, so you can audit and retire them safely.</p>
<p>The post <a href="https://www.ryanjadams.com/2026/09/manage-fabric-connections-at-scale-connection-recency/">Manage Fabric connections at scale with connection recency in Fabric REST APIs</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><em>Originally published on the Microsoft <a href="https://community.fabric.microsoft.com/blog/fbc_fabricupdatesblogs/manage-fabric-connections-at-scale-with-connection-recency-in-fabric-rest-apis/5363147" target="_blank" rel="noopener noreferrer">Fabric Updates Blog</a>. Reposted here for archive purposes.</em></p>



<h3 class="wp-block-heading">Understand Connection Recency</h3>



<p class="wp-block-paragraph">Connections are shared infrastructure in Microsoft Fabric. Pipelines, dataflows, semantic models, and other Fabric items use them to reach data sources without storing connection details and credentials in every item. Connection Recency adds the context administrators need to understand how each connection is being used. If you go to “Manage Connections and Gateways” in Fabric settings and open the settings of any connection, the following information card will populate:</p>



<figure class="wp-block-image size-large" style="max-width:429px;margin-left:0;text-align:left"><img data-recalc-dims="1" decoding="async" width="429" height="389" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/fabric-recency-1.png?resize=429%2C389&#038;ssl=1" alt="Manage Fabric connections at scale with connection recency in Fabric REST APIs — figure 1" class="wp-image-10021" style="width:100%;height:auto" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/fabric-recency-1.png?w=429&amp;ssl=1 429w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/fabric-recency-1.png?resize=300%2C272&amp;ssl=1 300w" sizes="auto, (max-width: 429px) 100vw, 429px" /><figcaption class="wp-block-image__caption" style="text-align:left">Connection Recency shows when a connection was created, last linked to an item, and last used to access credentials.</figcaption></figure>



<p class="wp-block-paragraph">The card provides three insights:&nbsp;</p>



<ul class="wp-block-list">
<li class=""><strong>Created on</strong> shows when the connection was created. </li>



<li class=""><strong>Last Bound to items</strong> shows when an item was most recently associated with the connection. <strong>By any user</strong> reflects activity across the tenant, and <strong>B</strong><strong>y me</strong> is specific to the viewer. </li>



<li class=""><strong>Last credentials</strong> <strong>used</strong> shows when a workload most recently used the connection credentials. It also separates activity <strong>B</strong><strong>y any user</strong> from activity <strong>B</strong><strong>y me</strong>, which is the current user. </li>
</ul>



<p class="wp-block-paragraph">Linking and usage are different events. A connection can be linked but never used, or it could have been linked a long time ago to an item that still runs every day. Credential-use information can also be up to 30 minutes behind real time, so use it for governance and investigation rather than immediate usage monitoring.&nbsp;</p>



<p class="wp-block-paragraph">Learn more in the <a href="https://learn.microsoft.com/fabric/data-factory/data-source-management#connection-recency-preview" target="_blank" rel="noopener nofollow noreferrer">Data source management documentation</a>.&nbsp;</p>



<h3 class="wp-block-heading">Why enterprise connection management becomes difficult&nbsp;</h3>



<p class="wp-block-paragraph">Connections can grow exponentially across a large organization. Every team, project, environment, gateway, and data source can introduce more connections. Over time, names become inconsistent, employees move on, and multiple connections can point to the same endpoint. The Fabric interface is useful for inspecting one connection, but reviewing recency and ownership one connection at a time does not scale to an enterprise inventory.&nbsp;</p>



<p class="wp-block-paragraph">This creates three common problems:&nbsp;</p>



<ul class="wp-block-list">
<li class=""><strong>Stale connections:</strong> A connection may no longer support an active workload, but its name, creation date, and Last Bound Date do not prove that it is unused. Administrators need both Last Bound Date and Last Credential Use signals before deciding what to review or retire. </li>



<li class=""><strong>Duplicate connections: </strong>Different display names can hide equivalent connection definitions. Duplicates increase credential maintenance, complicate troubleshooting, and make it harder to establish a preferred connection which dilutes the advantage of connection sharing and reuse across a tenant. </li>



<li class=""><strong>Ownership risk:</strong> A connection with only one person as an owner can become orphaned when that person leaves the organization. The connection might then be unavailable for administration or no longer usable by dependent workloads. </li>
</ul>



<p class="wp-block-paragraph">The REST APIs make it possible to evaluate these risks across every connection the caller can access. In the case of cloud connections, you can only see connections where you are the owner, but the admin for a gateway can see all connections on the gateway. This provides better scalability than the UI.&nbsp;</p>



<h3 class="wp-block-heading">Using the APIs to manage connections&nbsp;</h3>



<p class="wp-block-paragraph">You can use the APIs to identify stale and duplicate connections for removal and to help mitigate the issue of single user ownership. Gateways in Fabric have a limit of 1,000 connections, so managing and governing your connections will help you stay under that limit.&nbsp;&nbsp;</p>



<p class="wp-block-paragraph">Now that we understand each of the three scenarios, let’s see what the API can provide to help us handle each scenario:&nbsp;</p>



<ul class="wp-block-list">
<li class=""><strong>Stale connections: </strong>
<ul class="wp-block-list">
<li class=""><strong>LastBoundDateTime</strong> is the first of two signals we can use. If this is NULL, then it is currently not being used by a Fabric item and would be a candidate for removal. The one catch is that connections bound prior to recency being introduced will also show NULL, so we must filter out anything with a CreatedDateTime earlier than the introduction of the recency feature. Recency came out in preview at the end of March 2026, so my example code will filter anything created prior to May 1, 2026, to play it safe. </li>



<li class="">The second signal we have is the <strong>LastCredentialUsedDateTime</strong>, which indicates the last time the connection was used by any item. You can change this in the example code, but I’m going to use 90 days. You might have connections that legitimately get used less frequently, such as year-end runs or only during the holiday season, so keep that in mind when reviewing and adjust accordingly. </li>
</ul>
</li>



<li class=""><strong>Duplicate connections:</strong>
<ul class="wp-block-list">
<li class="">It is possible for two connections to have identical configurations except for the DisplayName. This defeats the purpose of having shareable connections, so we’ll need to use the <strong>LastCredentialUsedDateTime</strong> to determine which one was most recently used and flag any others to be considered for removal or consolidation. We’ll use ConnectionType, ConnectionPath, ConnectivityType, and GatewayID to identify duplicates. There are other columns you may want to add, such as CredentialType or ConnectionEncryption, depending on what you consider duplicate in your specific environment. </li>
</ul>
</li>



<li class=""><strong>Ownership risk:</strong>
<ul class="wp-block-list">
<li class="">For ownership continuity, you want to identify connections whose only <strong>Owner</strong> role assignment is a <strong>User</strong>, then add another approved owner. This helps avoid having an orphaned connection if an owner gets deleted. Adding a Microsoft Entra group is preferred because group membership can be maintained as people join, leave, or change responsibilities. A second individual owner is better than a single owner, but it does not provide the same durable operating model as group ownership. </li>
</ul>
</li>
</ul>



<div class="wp-block-group has-background has-global-padding is-layout-constrained wp-container-core-group-is-layout-14085c7e wp-block-group-is-layout-constrained" style="border-radius:6px;border-left-color:#b26a00;border-left-width:5px;background-color:#fff6e5;padding-top:1.1rem;padding-right:1.35rem;padding-bottom:1.1rem;padding-left:1.35rem">
<p class="has-text-color wp-block-paragraph" style="color:#8a5200;font-size:0.82rem;letter-spacing:0.08em;text-transform:uppercase"><strong>Myth</strong></p>



<p class="wp-block-paragraph">A common misconception is that a connection fails when its owner account is deleted. In reality, what matters is the credential being used for authentication. When you create a connection that uses OAuth, you become the owner and your OAuth credential is typically used. If your account is later deleted, the connection fails because it can no longer authenticate with that credential, not because ownership changed. If the connection instead uses an SPN and is shared with another user, it will continue to work.&nbsp;</p>
</div>



<p class="wp-block-paragraph">Explore the <a href="https://learn.microsoft.com/rest/api/fabric/core/connections/list-connections" target="_blank" rel="noopener nofollow noreferrer">List Connections</a>, <a href="https://learn.microsoft.com/rest/api/fabric/core/connections/list-connection-role-assignments" target="_blank" rel="noopener nofollow noreferrer">List Connection Role Assignments</a>, and <a href="https://learn.microsoft.com/rest/api/fabric/core/connections/add-connection-role-assignment" target="_blank" rel="noopener nofollow noreferrer">Add Connection Role Assignment</a> documentation for the complete request, pagination, identity, and permission details.</p>



<h3 class="wp-block-heading">Next steps&nbsp;</h3>



<p class="wp-block-paragraph">Run the <a href="https://github.com/microsoft/fabric-toolbox/tree/main/samples/fabric-connection-management" target="_blank" rel="noopener nofollow noreferrer">companion notebook</a> first. The notebook is designed as a Python notebook to be run in the Fabric Python runtime and not the PySpark Python runtime. There will be a section for each of the previous scenarios. Stale and duplicate connections will be shown so you can review before deciding what to remove, but that part will be up to you. You can use the data frame and pass the connection ID values to the <a href="https://learn.microsoft.com/rest/api/fabric/core/connections/delete-connection?tabs=HTTP" target="_blank" rel="noopener nofollow noreferrer">Delete Connection API</a>. If you don’t know how to write that part, then it’s a great opportunity to use Copilot.&nbsp;&nbsp;</p>



<p class="wp-block-paragraph">The ownership section will show you all connections with a single owner where you will want to add another owner. As a best practice, we’ll assume you are adding a group, but that part will be commented out as you very likely do not want to blatantly add the same group to every connection across the board. You can use the <a href="https://learn.microsoft.com/rest/api/fabric/core/connections/add-connection-role-assignment?tabs=HTTP" target="_blank" rel="noopener nofollow noreferrer">Add Connection Role Assignment API</a> to make those additions as you need.&nbsp;</p>



<p class="wp-block-paragraph">Connection recency data gives you the signals you need to manage connections at scale, but it should be the starting point for review rather than the basis for automatic removal. Use the <a href="https://github.com/microsoft/fabric-toolbox/tree/main/samples/fabric-connection-management" target="_blank" rel="noopener nofollow noreferrer">companion notebook</a> to identify potentially stale or duplicate connections and connections with a single owner, then apply your organization’s requirements before taking action. Regularly reviewing these signals can help you reduce unnecessary connections, improve ownership continuity, and establish a more manageable connection inventory.&nbsp;</p>
<p>The post <a href="https://www.ryanjadams.com/2026/09/manage-fabric-connections-at-scale-connection-recency/">Manage Fabric connections at scale with connection recency in Fabric REST APIs</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ryanjadams.com/2026/09/manage-fabric-connections-at-scale-connection-recency/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">10022</post-id>	</item>
		<item>
		<title>Connect securely to Snowflake in Fabric Pipelines and Copy Jobs</title>
		<link>https://www.ryanjadams.com/2026/09/connect-securely-to-snowflake-in-fabric-pipelines-and-copy-jobs/</link>
		
		<dc:creator><![CDATA[RYANJADAMS]]></dc:creator>
		<pubDate>Thu, 03 Sep 2026 14:00:00 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Microsoft Fabric]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Data Factory]]></category>
		<category><![CDATA[Private Endpoint]]></category>
		<category><![CDATA[Snowflake]]></category>
		<guid isPermaLink="false">https://www.ryanjadams.com/?p=10020</guid>

					<description><![CDATA[<p>Snowflake is a common source and sink for enterprise data movement, and Microsoft Fabric Data Factory supports it in both pipelines and Copy Jobs. Here is how Workspace-level Private Link, a private Azure Storage account, and Snowflake's USE_PRIVATELINK_ENDPOINT fit together to keep the whole path off the public internet.</p>
<p>The post <a href="https://www.ryanjadams.com/2026/09/connect-securely-to-snowflake-in-fabric-pipelines-and-copy-jobs/">Connect securely to Snowflake in Fabric Pipelines and Copy Jobs</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><em>Originally published on the Microsoft <a href="https://community.fabric.microsoft.com/blog/fbc_fabricupdatesblogs/connect-securely-to-snowflake-in-fabric-pipelines-and-copy-jobs/5363159" target="_blank" rel="noopener noreferrer">Fabric Updates Blog</a>. Reposted here for archive purposes.</em></p>
<p>Snowflake is a common source and sink for enterprise data movement, and Microsoft Fabric Data Factory supports it in both pipelines and Copy Jobs. For organizations that restrict public network access, some additional considerations are needed to meet that requirement. You can have private inbound and outbound access to Fabric, a private staging storage account, and a private Snowflake instance.</p>
<p>By combining Workspace-level Private Link, a private Azure Storage account, and Snowflake&#8217;s storage integration with USE_PRIVATELINK_ENDPOINT, you can keep the path entirely private. This post explains how those controls fit together for Fabric Data Factory pipelines and Copy Jobs.</p>
<h3>When this might apply</h3>
<p>Your organization might require private networking, where every hop needs to be inspectable and privately routed. Regulated workloads in financial services, healthcare, and public sector regularly require that analytics platforms have no public endpoints, that inbound and outbound traffic to storage stays inside a VNet, and that any third-party SaaS integration uses private links where supported. Snowflake supports Azure Private Link for storage endpoints and Fabric supports Workspace-level Private Link for inbound workspace access which can be combined to achieve a private path. Using each control for its intended direction avoids public exposure.</p>
<h3>The challenge</h3>
<p>The Snowflake connector in Fabric Data Factory uses Snowflake&#8217;s native COPY command under the hood and requires an interim storage account for data staging. Currently, staging is not supported by Onelake when using Workspace-level Private Link, so an external Azure Storage account can be used instead. Staged copy supports source or destination shapes that aren&#8217;t natively compatible with Snowflake&#8217;s COPY command and can improve throughput. The catch is the Azure Storage account sits between two systems that each have their own network story: Fabric on one side and Snowflake on the other. Locking down the copy path end-to-end means securing all three surfaces — the Fabric workspace, Azure Storage staging account, and Snowflake.</p>
<h3>The core model</h3>
<p>Think of the topology as two independent network paths that meet at the staging account.</p>

<figure class="wp-block-image size-large"><img data-recalc-dims="1" decoding="async" width="2179" height="1060" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/fabric-snowflake-1.png?resize=2179%2C1060&#038;ssl=1" alt="Connect securely to Snowflake in Fabric Pipelines and Copy Jobs — figure 1" class="wp-image-10019" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/fabric-snowflake-1.png?w=2179&amp;ssl=1 2179w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/fabric-snowflake-1.png?resize=300%2C146&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/fabric-snowflake-1.png?resize=1024%2C498&amp;ssl=1 1024w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/fabric-snowflake-1.png?resize=768%2C374&amp;ssl=1 768w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/fabric-snowflake-1.png?resize=1536%2C747&amp;ssl=1 1536w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/fabric-snowflake-1.png?resize=2048%2C996&amp;ssl=1 2048w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /><figcaption class="wp-block-image__caption">Private Path for Snowflake in Fabric Pipelines and Copy Jobs.</figcaption></figure>


<ul>
<li><strong>Inbound Access to the Fabric Workspace</strong> — Workspace-level Private Link maps the workspace to an approved VNet and lets you deny public inbound access. It doesn&#8217;t place the Fabric workspace inside your VNet or govern outbound data-source traffic.</li>
<li><strong>Azure Blob Staging</strong> — Trusted Workspace Access provides a private connection from the Fabric Workspace to your storage account. Not only is it private, but it uses a Workspace Identity to authenticate and a Resource Instance rule to only allow specific workspaces.</li>
<li><strong>Snowflake to Staging Storage</strong> — Snowflake provisions a private endpoint into your Azure tenant against the same staging account. The Snowflake storage integration is created with USE_PRIVATELINK_ENDPOINT = TRUE so Snowflake&#8217;s own copy operations honor the private path.</li>
<li><strong>Public Internet Access</strong> — Public network access on the storage account and Fabric Workspace is blocked.</li>
</ul>
<p>Network access and authorization are separate. Snowflake&#8217;s storage integration provisions a service principal in Azure. This SPN will need Storage Blob Data Contributor permission on the staging storage account. The Fabric Azure Blob connection uses the authentication configured for that connection, such as workspace identity. That identity will need Storage Blob Data Reader on the staging storage account.</p>
<h3>Setup</h3>
<p>The full configuration breaks into five phases:</p>
<ol>
<li><strong>Build the Network Foundation</strong>. Create a workspace, Azure private link service, virtual network, virtual machine, private endpoint from the workspace to the private link service and finally turn off public networking to the workspace. Complete instructions for all these items can be found in the <a href="https://learn.microsoft.com/fabric/security/security-workspace-level-private-links-set-up" target="_blank" rel="noopener nofollow noreferrer">Set up and use Workspace-level Private Links</a> documentation.</li>
<li><strong>Create Staging Storage Account</strong>. Use these instructions to <a href="https://learn.microsoft.com/azure/storage/common/storage-account-create?toc=/azure/storage/blobs/toc.json&amp;bc=/azure/storage/blobs/breadcrumb/toc.json" target="_blank" rel="noopener nofollow noreferrer">create an Azure storage account</a>. Once you have an account you need to <a href="https://learn.microsoft.com/fabric/security/security-trusted-workspace-access#configure-trusted-workspace-access-in-adls-gen2" target="_blank" rel="noopener nofollow noreferrer">configure Trusted Workspace Access</a> for private networking between your Fabric Workspace and the staging storage account.</li>
<li><strong>Configure the Snowflake side</strong>. In Snowflake, <a href="https://docs.snowflake.com/user-guide/private-manage-endpoints-azure" target="_blank" rel="noopener nofollow noreferrer">create a private connectivity endpoint</a> that points at the external staging container and set USE_PRIVATELINK_ENDPOINT = TRUE. Snowflake provisions a service principal in your Azure tenant on your behalf. Grant that service principal the Storage Blob Data Contributor role on the staging account. This lets Snowflake read from and write to staging over the private endpoint using its own identity.</li>
<li><strong>Close the public paths</strong>. You should have already denied public inbound access to the Fabric workspace as the last part of the instructions provided in step 1. You also need to disable public network access to the staging account by <a href="https://learn.microsoft.com/azure/storage/common/storage-network-security-set-default-access?toc=/azure/storage/blobs/toc.json&amp;bc=/azure/storage/blobs/breadcrumb/toc.json" target="_blank" rel="noopener nofollow noreferrer">setting the default public network access rule</a> to <strong>disabled</strong>.</li>
<li><strong>Create Copy Activity or Copy Job</strong>. Create your pipeline with Copy Activity or, even better, <a href="https://learn.microsoft.com/fabric/data-factory/create-copy-job" target="_blank" rel="noopener nofollow noreferrer">create a Copy Job</a>. Then select Snowflake as the source or destination, enable external staging, select the staging storage account, enter the required storage path, choose a destination, and run the item.</li>
</ol>
<h3>Boundaries and things to check</h3>
<p>A few constraints are worth calling out before you plan a rollout:</p>
<ul>
<li><strong>Workspace-level Private Link prerequisites</strong>. Confirm that your Fabric capacity SKU and region support Workspace-level Private Link, and that your tenant admin has enabled the required tenant settings.</li>
<li><strong>Staging is Azure Blob</strong>. External staging for Snowflake in the Copy Activity uses an Azure Blob container. You don&#8217;t need to enable hierarchical namespace on the account for staging to function.</li>
<li><strong>Snowflake USE_PRIVATELINK_ENDPOINT requires a supported Snowflake account</strong>. Snowflake-managed outbound private connectivity endpoints require Business Critical edition or higher. Confirm that the feature is enabled for your Snowflake account before provisioning the endpoint.</li>
<li><strong>Private Endpoint Approval</strong>. The private endpoint from Snowflake into your storage account will be in a pending state and must be approved on the storage account.</li>
</ul>
<h3>Next steps</h3>
<p>If you&#8217;re evaluating Snowflake as a source or sink in Fabric today, deploy this topology end-to-end in a non-production workspace before you promote it. The moving parts are simple individually, but each one must be in place for the private path to hold.</p>
<p>Learn more from the following documentation:</p>
<ul>
<li><a href="https://learn.microsoft.com/fabric/security/security-workspace-level-private-links-set-up" target="_blank" rel="noopener nofollow noreferrer">Set up and use Workspace-level Private Links in Microsoft Fabric</a></li>
<li><a href="https://learn.microsoft.com/fabric/data-factory/connector-snowflake-copy-activity" target="_blank" rel="noopener nofollow noreferrer">Configure Snowflake in a copy activity in Fabric Data Factory</a></li>
<li><a href="https://learn.microsoft.com/fabric/data-factory/workspace-identity" target="_blank" rel="noopener nofollow noreferrer">Use workspace identity in Fabric Data Factory</a></li>
<li><a href="https://learn.microsoft.com/fabric/security/security-trusted-workspace-access" target="_blank" rel="noopener nofollow noreferrer">Configure trusted workspace access in Microsoft Fabric</a></li>
<li><a href="https://docs.snowflake.com/user-guide/data-load-azure-config" target="_blank" rel="noopener nofollow noreferrer">Snowflake: Configuring an Azure container for loading data</a></li>
<li><a href="https://docs.snowflake.com/user-guide/private-manage-endpoints-azure" target="_blank" rel="noopener nofollow noreferrer">Snowflake: Managing Azure Private Link endpoints</a></li>
</ul>
<p>The post <a href="https://www.ryanjadams.com/2026/09/connect-securely-to-snowflake-in-fabric-pipelines-and-copy-jobs/">Connect securely to Snowflake in Fabric Pipelines and Copy Jobs</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">10020</post-id>	</item>
		<item>
		<title>Disabling Public Network Access in Synapse</title>
		<link>https://www.ryanjadams.com/2023/02/disabling-public-network-access-in-synapse/</link>
		
		<dc:creator><![CDATA[RYANJADAMS]]></dc:creator>
		<pubDate>Thu, 09 Feb 2023 15:00:00 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Synapse]]></category>
		<category><![CDATA[Endpoint]]></category>
		<category><![CDATA[Private Endpoint]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://www.ryanjadams.com/?p=9960</guid>

					<description><![CDATA[<p>Disabling public network access on a Synapse workspace throws a 403 in Synapse Studio. Here is why, and how private endpoints fix it.</p>
<p>The post <a href="https://www.ryanjadams.com/2023/02/disabling-public-network-access-in-synapse/">Disabling Public Network Access in Synapse</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><em>Originally published on the Microsoft <a href="https://techcommunity.microsoft.com/blog/azuresynapseanalyticsblog/disabling-public-network-access-in-synapse/3692197" target="_blank" rel="noopener noreferrer">Azure Synapse Analytics Blog</a>. Reposted here for archive purposes.</em></p>
<p>If you disable public access to your Azure Synapse Workspace you will get the following error message when attempting to open Synapse Studio. </p>
<p>“Failed to load one or more resources due to forbidden issue, error code 403.” </p>
<p><img data-recalc-dims="1" decoding="async" width="371" height="345" loading="lazy" class="wp-image-9947" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-2.png?resize=371%2C345&#038;ssl=1" alt="Disabling Public Network Access in Synapse — figure 2" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-2.png?w=371&amp;ssl=1 371w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-2.png?resize=300%2C279&amp;ssl=1 300w" sizes="auto, (max-width: 371px) 100vw, 371px" /></p>
<p>If you click on details, you’ll see the following: </p>
<p><img data-recalc-dims="1" decoding="async" width="615" height="395" loading="lazy" class="wp-image-9948" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-3.png?resize=615%2C395&#038;ssl=1" alt="Disabling Public Network Access in Synapse — figure 3" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-3.png?w=615&amp;ssl=1 615w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-3.png?resize=300%2C193&amp;ssl=1 300w" sizes="auto, (max-width: 615px) 100vw, 615px" /></p>
<p>Most users end up with this error page and don’t know that disabling Public Network Access was the cause and that makes troubleshooting much more difficult.  Receiving an error after disabling Public Network Access is by design and expected.</p>
<p>The first thing we see is the error code being 403.  That’s an HTTP/HTTPS Forbidden error code and not a code from the Synapse service. This indicates the issue is accessing the service via HTTP/HTTPS and most likely network related. </p>
<p>In this state, you might also see your SQL Pools show to be “<strong>Unavailable</strong>” in the <strong>Manage Hub</strong>. If you click <strong>OK</strong> on the error message, select the <strong>Manage Hub</strong> on the left, and <strong>SQL Pools</strong> under the <strong>Analytics Pools</strong> section you can view the status of your SQL Pools. </p>
<p><img data-recalc-dims="1" decoding="async" width="1116" height="322" loading="lazy" class="wp-image-9949" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-4.png?resize=1116%2C322&#038;ssl=1" alt="Disabling Public Network Access in Synapse — figure 4" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-4.png?w=1116&amp;ssl=1 1116w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-4.png?resize=300%2C87&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-4.png?resize=1024%2C295&amp;ssl=1 1024w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-4.png?resize=768%2C222&amp;ssl=1 768w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /></p>
<h2>Disable Public Network Access </h2>
<p>You can easily verify if Public Network Access has been enabled or not in the Azure portal.  Simply navigate to your Synapse Workspace resource and go to the networking section.  The very first option has a radio button indicating if Public Network Access is enabled or disabled. If it is set to disabled, you’ll see an information icon informing you that private endpoints will be required. </p>
<p><img data-recalc-dims="1" decoding="async" width="832" height="714" loading="lazy" class="wp-image-9950" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-5.png?resize=832%2C714&#038;ssl=1" alt="Disabling Public Network Access in Synapse — figure 5" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-5.png?w=832&amp;ssl=1 832w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-5.png?resize=300%2C257&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-5.png?resize=768%2C659&amp;ssl=1 768w" sizes="auto, (max-width: 832px) 100vw, 832px" /></p>
<p>It&#8217;s important to note that enabling Public Network Access is not opening your workspace up to the entire internet.  If you enable it, the service puts a firewall in front of it.  However, you do need to be aware that the default <strong>allowAll</strong> range (enabled during workspace creation) is indeed the entire internet.  You simply need to change the default rule to only allow your trusted networks, or delete it and create new allow rules.  In addition, we use <a href="https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/routing-preference-overview" target="_blank" rel="noopener noreferrer">cold potato routing</a> so if all your allowed networks are in Azure then the traffic never leaves the Azure backbone. </p>
<p><img data-recalc-dims="1" decoding="async" width="560" height="166" loading="lazy" class="wp-image-9951" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-6.png?resize=560%2C166&#038;ssl=1" alt="Disabling Public Network Access in Synapse — figure 6" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-6.png?w=560&amp;ssl=1 560w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-6.png?resize=300%2C89&amp;ssl=1 300w" sizes="auto, (max-width: 560px) 100vw, 560px" /></p>
<h2>Create private endpoints </h2>
<p>If you have chosen to disable Public Network Access and are receiving the above error message when opening the portal, then you will need to create Private Endpoints to access your workspace.  This can sometimes be confusing as there are two places where Private Endpoints can be created so we need to understand the difference.  The first location is inside the workspace under the <strong>Manage Hub</strong>.  Depending on the resources you have created, you will likely see three entries already there.  These are automatically created for you so the workspace can access Storage, Dedicated Pools, and Serverless Pools. </p>
<p><strong>The important thing to remember is that these private endpoints </strong><strong>allow the Synapse workspace to access resources outside itself. </strong></p>
<p><img data-recalc-dims="1" decoding="async" width="1201" height="506" loading="lazy" class="wp-image-9952" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-7.png?resize=1201%2C506&#038;ssl=1" alt="Disabling Public Network Access in Synapse — figure 7" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-7.png?w=1201&amp;ssl=1 1201w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-7.png?resize=300%2C126&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-7.png?resize=1024%2C431&amp;ssl=1 1024w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-7.png?resize=768%2C324&amp;ssl=1 768w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /></p>
<p>The second location is in the Azure Portal on the Synapse Workspace resource.  By default, this will be empty and is where you need to create private endpoints to resolve the issue in our original error message. </p>
<p><img data-recalc-dims="1" decoding="async" width="761" height="497" loading="lazy" class="wp-image-9953" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-8.png?resize=761%2C497&#038;ssl=1" alt="Disabling Public Network Access in Synapse — figure 8" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-8.png?w=761&amp;ssl=1 761w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-8.png?resize=300%2C196&amp;ssl=1 300w" sizes="auto, (max-width: 761px) 100vw, 761px" /></p>
<p><strong>The important thing to remember is private endpoints allow things outside the workspace to access things inside the workspace. </strong></p>
<p>There are three private endpoints you will want to create unless you do not plan to use a Dedicated Pool or Serverless Pool.  Each private endpoint will need to be connected to a target VNET, which should be the network location from where you plan to access your workspace. </p>
<p><img data-recalc-dims="1" decoding="async" width="784" height="516" loading="lazy" class="wp-image-9954" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-9.png?resize=784%2C516&#038;ssl=1" alt="Disabling Public Network Access in Synapse — figure 9" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-9.png?w=784&amp;ssl=1 784w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-9.png?resize=300%2C197&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-9.png?resize=768%2C505&amp;ssl=1 768w" sizes="auto, (max-width: 784px) 100vw, 784px" /></p>
<p>On the Virtual Network tab, you select the VNET from where you plan to manage your workspace and on the DNS tab, you need to select “Yes” to integrate with a Private DNS Zone. If you are already running your own DNS implementation, then can alternately create &#8220;A&#8221; records for the Private Endpoints there.</p>
<p><img data-recalc-dims="1" decoding="async" width="773" height="352" loading="lazy" class="wp-image-9955" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-10.png?resize=773%2C352&#038;ssl=1" alt="Disabling Public Network Access in Synapse — figure 10" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-10.png?w=773&amp;ssl=1 773w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-10.png?resize=300%2C137&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-10.png?resize=768%2C350&amp;ssl=1 768w" sizes="auto, (max-width: 773px) 100vw, 773px" /></p>
<p>Once the “Dev” target sub-resource is created the error on the workspace portal will be resolved.  Once the <strong>SqlOnDemand</strong> target sub-resource is created, the Serverless Pool will turn from “Unavailable” to “Online”. Once the <strong>Sql </strong>target sub-resource is created, the Dedicated Pool will turn from “Unavailable” to “Online”. </p>
<h2>What is inside and outside of a managed workspace VNET </h2>
<p>Now that you know there are two places to create private endpoints depending on whether it is traffic coming into or going out of the workspace, let’s take a closer look at what is inside and outside of the Synapse Managed Workspace VNET. </p>
<p>The short answer is Spark pools are inside the workspace VNET while serverless and dedicated pools are outside the workspace VNET.  This is why you get private endpoints created in the workspace automatically for serverless and dedicated pools.  Review the diagram below to get a good visualization of what this looks like.  I also highly recommend reading a post by fellow colleague Benjamin Leroux on <a href="https://techcommunity.microsoft.com/t5/azure-architecture-blog/understanding-azure-synapse-private-endpoints/ba-p/2281463" target="_blank" rel="noopener">Understanding Azure Synapse Private Endpoints</a>. </p>
<p><img data-recalc-dims="1" decoding="async" width="998" height="655" loading="lazy" class="wp-image-9956" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-11.png?resize=998%2C655&#038;ssl=1" alt="Disabling Public Network Access in Synapse — figure 11" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-11.png?w=998&amp;ssl=1 998w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-11.png?resize=300%2C197&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-11.png?resize=768%2C504&amp;ssl=1 768w" sizes="auto, (max-width: 998px) 100vw, 998px" /></p>
<h2>Accessing the private endpoint through peered VNETs </h2>
<p>A common practice in cloud networking is to use a <a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/hub-spoke-network-topology" target="_blank" rel="noopener noreferrer">hub and spoke architecture</a>.  In this case, you will want to create your private endpoints in the hub network.  Any other VNET or spoke that is peered to the hub will be able to access the workspace.  Here is an example of what that might look like: </p>
<p><img data-recalc-dims="1" decoding="async" width="435" height="371" loading="lazy" class="wp-image-9957" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-12.png?resize=435%2C371&#038;ssl=1" alt="Disabling Public Network Access in Synapse — figure 12" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-12.png?w=435&amp;ssl=1 435w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-12.png?resize=300%2C256&amp;ssl=1 300w" sizes="auto, (max-width: 435px) 100vw, 435px" /></p>
<p>When using private endpoints, you will need a <strong>private DNS zone</strong> or your own DNS so applications can resolve the endpoint names.  There is an extra step needed to link your spoke VNETs to the private DNS zone in your hub network.  Go to the private DNS zone in your hub network and select “<strong>Virtual Network Links</strong>” (You will see a link already created for the hub VNET) and then click “<strong>Add</strong>”.  Simply name your link and select the spoke network from the drop-down menu. </p>
<p><img data-recalc-dims="1" decoding="async" width="746" height="400" loading="lazy" class="wp-image-9958" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-13.png?resize=746%2C400&#038;ssl=1" alt="Disabling Public Network Access in Synapse — figure 13" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-13.png?w=746&amp;ssl=1 746w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-13.png?resize=300%2C161&amp;ssl=1 300w" sizes="auto, (max-width: 746px) 100vw, 746px" /></p>
<h2>Creating multiple private endpoints </h2>
<p>If you are not using a hub and spoke network architecture or prefer to not rely on VNET peering, there is one last choice.  You can create private endpoints in each VNET where you plan to manage your workspace.  Remember that you most likely will need to create three private endpoints (one each for the portal or Dev, Dedicated Pools, and Serverless Pools) in each VNET.  If you have 10 VNETs that will need to access the workspace, then you must create 30 Private Endpoints. </p>
<p>For this scenario, you can simply follow the instructions provided above for creating the private endpoints.  You must repeat it for every spoke network that you want to have access to your Synapse workspace.  The other difference will be DNS.  Since there is no peering involved, services in spoke networks cannot access the Private DNS Zone in the hub network.  You will need to create a Private DNS zone in each network or utilize a centralized DNS. </p>
<p>Here is what that might look like when utilizing private DNS zones: </p>
<p><img data-recalc-dims="1" decoding="async" width="515" height="380" loading="lazy" class="wp-image-9959" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-14.png?resize=515%2C380&#038;ssl=1" alt="Disabling Public Network Access in Synapse — figure 14" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-14.png?w=515&amp;ssl=1 515w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-public-network-14.png?resize=300%2C221&amp;ssl=1 300w" sizes="auto, (max-width: 515px) 100vw, 515px" /></p>
<h2> </h2>
<h2>Demo</h2>
<p>Watch this <a href="https://www.youtube.com/watch?v=jF0Vq1XxY0c" target="_self" rel="nofollow noopener noreferrer">video</a> to learn how to disable public network access.</p>


<figure class="wp-embed-aspect-16-9 wp-has-aspect-ratio wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube"><div class="wp-block-embed__wrapper">
<iframe title="Azure Synapse - Disable Public Network Access" width="500" height="281" src="https://www.youtube.com/embed/jF0Vq1XxY0c?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>


<h2>Conclusion </h2>
<p>You have several options for <a href="https://learn.microsoft.com/en-us/azure/synapse-analytics/guidance/security-white-paper-network-security" target="_blank" rel="noopener noreferrer">securing network access to your Azure Synapse Workspaces</a>. </p>
<ul>
<li>Enable Public Network Access and use the built-in firewall to only allow trusted networks.  </li>
<li>Use private endpoints and manage the workspace from a single VNET. </li>
<li>Use private endpoints and access the workspace through peered VNETs. </li>
<li>Use private endpoints in every VNET that needs access to the workspace. </li>
</ul><p>The post <a href="https://www.ryanjadams.com/2023/02/disabling-public-network-access-in-synapse/">Disabling Public Network Access in Synapse</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">9960</post-id>	</item>
		<item>
		<title>How to read Data Lake with Synapse Serverless – Part 2</title>
		<link>https://www.ryanjadams.com/2023/01/read-data-lake-synapse-serverless-part-2/</link>
		
		<dc:creator><![CDATA[RYANJADAMS]]></dc:creator>
		<pubDate>Thu, 26 Jan 2023 15:00:00 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Synapse]]></category>
		<category><![CDATA[Data Lake]]></category>
		<category><![CDATA[Serverless]]></category>
		<guid isPermaLink="false">https://www.ryanjadams.com/?p=9945</guid>

					<description><![CDATA[<p>Use a Synapse Serverless external table to query a path in your Azure Data Lake with plain T-SQL. Part 2 of two.</p>
<p>The post <a href="https://www.ryanjadams.com/2023/01/read-data-lake-synapse-serverless-part-2/">How to read Data Lake with Synapse Serverless – Part 2</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><em>Originally published on the Microsoft <a href="https://techcommunity.microsoft.com/blog/azuresynapseanalyticsblog/how-to-read-data-lake-with-synapse-serverless-%E2%80%93-part-2/3692806" target="_blank" rel="noopener noreferrer">Azure Synapse Analytics Blog</a>. Reposted here for archive purposes.</em></p>
<p>There are two ways to read data inside Data Lake using the Synapse Serverless engine.  In this article, we’ll look at the second method which <strong>uses an external table to query a path within the lake</strong>.  To learn how to use the OPENROWSET command, check out <a href="https://www.ryanjadams.com/2023/01/read-data-lake-synapse-serverless-part-1/" target="_self">part 1</a> of the article series. </p>
<p>Synapse is a collection of tools with four different analytical engines (<a href="https://learn.microsoft.com/en-us/azure/synapse-analytics/sql-data-warehouse/sql-data-warehouse-overview-what-is" target="_blank" rel="noopener noreferrer">Dedicated Pool</a>, <a href="https://learn.microsoft.com/en-us/azure/synapse-analytics/spark/apache-spark-overview" target="_blank" rel="noopener noreferrer">Spark Pool</a>, <a href="https://learn.microsoft.com/en-us/azure/synapse-analytics/sql/on-demand-workspace-overview" target="_blank" rel="noopener noreferrer">Serverless Pool</a>, <a href="https://learn.microsoft.com/en-us/azure/synapse-analytics/data-explorer/data-explorer-overview" target="_blank" rel="noopener noreferrer">Data Explorer Pool</a>).  This gives you a lot of options for ingesting, transforming, storing, and querying your data.  Here you will use the Synapse Serverless Pool to query the data in your ADLS account.   </p>
<h2>Create a data source</h2>
<p>Go to the <strong>Develop Hub</strong> and click the ellipsis that appears when you hover your cursor over the SQL Scripts heading.  That will open a drop-down menu where you can select “<strong>New SQL Script</strong>”.   </p>
<p><img data-recalc-dims="1" decoding="async" width="1285" height="345" loading="lazy" class="wp-image-9944" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-serverless-p2-2.png?resize=1285%2C345&#038;ssl=1" alt="How to read Data Lake with Synapse Serverless – Part 2 — figure 2" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-serverless-p2-2.png?w=1285&amp;ssl=1 1285w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-serverless-p2-2.png?resize=300%2C81&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-serverless-p2-2.png?resize=1024%2C275&amp;ssl=1 1024w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-serverless-p2-2.png?resize=768%2C206&amp;ssl=1 768w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /></p>
<p>There are three major components we need to create before we can query our table.  We need to create a <strong>Data Source</strong>, a <strong>File Format</strong>, and the <strong>External Table</strong> itself. Enter the code below into your script, make sure the “<strong>Connect To</strong>” box shows “<strong>Built-in</strong>” to use the serverless pool.  You also need to select your database in the “<strong>Use Database</strong>” box, where the metadata will be stored for everything we are about to create. This first part will create the Data Source which defines the location in the data lake where the files to be queried are stored. </p>

<pre class="language-sql wp-block-code"><span><code class="hljs language-sql"><span class="hljs-comment">/* Create an external data source. You only do this once. */</span> 
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">EXTERNAL</span> <span class="hljs-keyword">DATA</span> <span class="hljs-keyword">SOURCE</span> adls_temperature 
<span class="hljs-keyword">WITH</span> 
(     
    LOCATION = <span class="hljs-string">'https://MyLake.dfs.core.windows.net/iot/bronze/'</span> 
); 
GO </code></span></pre>

<h2>Create file format </h2>
<p>Next, we need to create a file format.  This lets the engine know what type of format it should expect for our files.  In this example, the files are in Parquet format, but you could also use DELIMITEDTEXT format for CSV type files.  There are quite a few more options required for CSV type files so check out the <a href="https://learn.microsoft.com/en-us/azure/synapse-analytics/sql/develop-tables-external-tables?tabs=native#create-external-file-format" target="_blank" rel="noopener noreferrer">Full Documentation</a> for creating an external file format. </p>

<pre class="language-sql wp-block-code"><span><code class="hljs language-sql"><span class="hljs-comment">/* Create an external file format. You only do this once. */</span> 
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">EXTERNAL</span> <span class="hljs-keyword">FILE</span> <span class="hljs-keyword">FORMAT</span> parquet_file_format 
<span class="hljs-keyword">WITH</span> 
(   
    FORMAT_TYPE = PARQUET 
);
GO</code></span></pre>

<h2> </h2>
<h2>Create external table </h2>
<p>Now we can finally create our table using the <strong>CREATE EXTERNAL TABLE</strong> command.  In addition to defining our columns like we would normally do when creating a table, we also have a <strong>WITH</strong> clause.  Here we input the names we used above for the <strong>Data Source</strong> and <strong>File Format </strong>along with the <strong>Location</strong>.  The root or starting folder is what we defined in the <strong>Data Source,</strong> so the <strong>Location</strong> is a relative path to that root folder.  You will also see two asterisks at the end of the location path which indicate to include every sub folder in that path.  If you do not want to include sub folders, then simply remove the asterisks. </p>

<pre class="language-sql wp-block-code"><span><code class="hljs language-sql"><span class="hljs-comment">/* Create an external table. You only do this once. */</span> 
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">EXTERNAL</span> <span class="hljs-keyword">TABLE</span> Temperature( 
    &#91;temp1] <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>), 
    &#91;temp2] <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">100</span>), 
    &#91;ArrivalTime] datetime2(<span class="hljs-number">7</span>) 
    ) 
<span class="hljs-keyword">WITH</span> ( 
    LOCATION = <span class="hljs-string">'temperature_raw/**'</span>, 
    DATA_SOURCE = adls_temperature,   
    FILE_FORMAT = parquet_file_format 
);
GO</code></span></pre>

<h2>Query External Table </h2>
<p>Our setup is now complete, and our table is ready to query.  Note that the <strong>Data Source</strong> can be reused with a different relative path for other folders under that root.  The <strong>File Format</strong> can also be reused for Parquet files. Now it’s time to query our new External Table. </p>

<pre class="language-sql wp-block-code"><span><code class="hljs language-sql"><span class="hljs-comment">/* Query the new external table you created. All data is still stored in ADLS */</span> 
<span class="hljs-keyword">SELECT</span> temp1, temp2, &#91;ArrivalTime] <span class="hljs-keyword">FROM</span> Temperature 
<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> &#91;ArrivalTime] <span class="hljs-keyword">DESC</span>; </code></span></pre>

<h2>Demo</h2>
<p>Learn how to read Data Lake using Synapse Serverless external tables with this <a href="https://www.youtube.com/watch?v=ANfua6Tctrs" target="_self" rel="nofollow noopener noreferrer">video</a>.</p>


<figure class="wp-embed-aspect-16-9 wp-has-aspect-ratio wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube"><div class="wp-block-embed__wrapper">
<iframe title="Read Data Lake with Synapse Serverless - Part 2" width="500" height="281" src="https://www.youtube.com/embed/ANfua6Tctrs?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>


<h2>Conclusion </h2>
<p>In this article we learned how to use the Synapse Serverless engine to query data in our data lake.  This is one of two ways to perform this task.  The first method uses the <a href="https://techcommunity.microsoft.com/t5/azure-synapse-analytics-blog/how-to-read-data-lake-with-synapse-serverless-part-1/ba-p/3692779" target="_self">OPENROWSET Command</a> and is great for ad hoc queries.  The method described in this article allows us to persist the metadata of the configuration for easier reuse.  Now you don’t need to remember the path or format and can simply query the table like any other table in your database. To continue learning more about serverless, make sure to check out our documentation on <a href="https://learn.microsoft.com/en-us/azure/synapse-analytics/sql/best-practices-serverless-sql-pool" target="_blank" rel="noopener noreferrer">Best Practices for Serverless SQL Pool</a>.</p><p>The post <a href="https://www.ryanjadams.com/2023/01/read-data-lake-synapse-serverless-part-2/">How to read Data Lake with Synapse Serverless – Part 2</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">9945</post-id>	</item>
		<item>
		<title>How to read Data Lake with Synapse Serverless – Part 1</title>
		<link>https://www.ryanjadams.com/2023/01/read-data-lake-synapse-serverless-part-1/</link>
		
		<dc:creator><![CDATA[RYANJADAMS]]></dc:creator>
		<pubDate>Thu, 19 Jan 2023 15:00:00 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Synapse]]></category>
		<category><![CDATA[Data Lake]]></category>
		<category><![CDATA[Serverless]]></category>
		<guid isPermaLink="false">https://www.ryanjadams.com/?p=9942</guid>

					<description><![CDATA[<p>Query files in your Azure Data Lake with the Synapse Serverless pool using OPENROWSET, with no infrastructure to provision. Part 1 of two.</p>
<p>The post <a href="https://www.ryanjadams.com/2023/01/read-data-lake-synapse-serverless-part-1/">How to read Data Lake with Synapse Serverless – Part 1</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><em>Originally published on the Microsoft <a href="https://techcommunity.microsoft.com/blog/azuresynapseanalyticsblog/how-to-read-data-lake-with-synapse-serverless-%E2%80%93-part-1/3692779" target="_blank" rel="noopener noreferrer">Azure Synapse Analytics Blog</a>. Reposted here for archive purposes.</em></p>
<p>There are two ways to read data inside Data Lake using the Synapse Serverless engine.  In this article, we’ll look at the first method which uses OPENROWSET to query a path within the lake. To learn how to use an external table to query a path within the lake, check out the <a href="https://www.ryanjadams.com/2023/01/read-data-lake-synapse-serverless-part-2/" target="_self">part 2</a> of the article series. </p>
<p>Synapse is a collection of tools with four different analytical engines (<a href="https://learn.microsoft.com/en-us/azure/synapse-analytics/sql-data-warehouse/sql-data-warehouse-overview-what-is" target="_blank" rel="noopener noreferrer">Dedicated Pool</a>, <a href="https://learn.microsoft.com/en-us/azure/synapse-analytics/spark/apache-spark-overview" target="_blank" rel="noopener noreferrer">Spark Pool</a>, <a href="https://learn.microsoft.com/en-us/azure/synapse-analytics/sql/on-demand-workspace-overview" target="_blank" rel="noopener noreferrer">Serverless Pool</a>, <a href="https://learn.microsoft.com/en-us/azure/synapse-analytics/data-explorer/data-explorer-overview" target="_blank" rel="noopener noreferrer">Data Explorer Pool</a>).  This gives you a lot of options for ingesting, transforming, storing, and querying your data.  The article will focus on how you can use the Synapse Serverless Pool to query the data in your ADLS account.   </p>
<p>Let&#8217;s start by going to the <strong>Develop Hub</strong> and click the ellipsis that appears when you hover your cursor over the SQL Scripts heading.  That will open a drop-down menu where you can select “<strong>New SQL Script</strong>”.  Enter the code below into your script, make sure the “<strong>Connect To</strong>” box shows “<strong>Built-in</strong>” to use the serverless pool, and execute the script to read your data. </p>
<p><img data-recalc-dims="1" decoding="async" width="1286" height="294" loading="lazy" class="wp-image-9941" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-serverless-p1-2.png?resize=1286%2C294&#038;ssl=1" alt="How to read Data Lake with Synapse Serverless – Part 1 — figure 2" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-serverless-p1-2.png?w=1286&amp;ssl=1 1286w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-serverless-p1-2.png?resize=300%2C69&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-serverless-p1-2.png?resize=1024%2C234&amp;ssl=1 1024w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2026/09/synapse-serverless-p1-2.png?resize=768%2C176&amp;ssl=1 768w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /></p>
<p>In my example, I have some IoT data representing temperatures from two different probes.  The data is stored in my lake using Parquet format.  Here are the options I have defined: </p>
<ul>
<li><strong>BULK</strong> – This is the URI to my data lake and you can see I used a wildcard to grab all files ending in, .parquet (*.parquet).  If you only point to a folder or container, then it will read all files in that location. </li>
<li><strong>FORMAT</strong> – The OPENROWSET function supports three file formats (Parquet, Delta, and CSV) and I am telling it to expect Parquet. </li>
</ul>
<p>The last thing I do is order my results, so the newest events are at the top.  You can easily use the following code and change it to fit your environment and start querying data in your lake using the SQL language! </p>

<pre class="language-sql wp-block-code"><span><code class="hljs language-sql"><span class="hljs-keyword">SELECT</span> 
    Temp1, Temp2, ArrivalTime 
<span class="hljs-keyword">FROM</span> 
    OPENROWSET( 
        <span class="hljs-keyword">BULK</span> <span class="hljs-string">'https://YourStorageAccountName.dfs.core.windows.net/iot/bronze/temperature_raw/*.parquet'</span>, 
        <span class="hljs-keyword">FORMAT</span> = <span class="hljs-string">'PARQUET'</span> 
    ) <span class="hljs-keyword">AS</span> &#91;PARQUET <span class="hljs-keyword">FORMAT</span>] 
<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> ArrivalTime <span class="hljs-keyword">DESC</span></code></span></pre>

<h2>Demo</h2>
<p>Learn how to read Data Lake using OPENROWSET by checking out this <a href="https://www.youtube.com/watch?v=2tOxJlpcwko" target="_self" rel="nofollow noopener noreferrer">video</a>. </p>


<figure class="wp-embed-aspect-16-9 wp-has-aspect-ratio wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube"><div class="wp-block-embed__wrapper">
<iframe title="Read Data Lake with Synapse Serverless - Part 1" width="500" height="281" src="https://www.youtube.com/embed/2tOxJlpcwko?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>


<h2> </h2>
<h2> </h2>
<h2>Resources </h2>
<p>You can find more details and full documentation on the OPENROWSET command with this <a href="https://learn.microsoft.com/en-us/azure/synapse-analytics/sql/develop-openrowset" target="_blank" rel="noopener noreferrer">link</a>.  For increased performance, it’s worth noting that we have two different parsers you can use, and you define that with the PARSER_VERSION option.  Check out an excellent video demonstration we have on <a href="https://www.youtube.com/watch?v=HpBpFH-l11E" target="_blank" rel="noopener nofollow noreferrer">CSV files and the two different parser versions</a>. </p><p>The post <a href="https://www.ryanjadams.com/2023/01/read-data-lake-synapse-serverless-part-1/">How to read Data Lake with Synapse Serverless – Part 1</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">9942</post-id>	</item>
		<item>
		<title>Structured Streaming in Synapse Spark</title>
		<link>https://www.ryanjadams.com/2023/01/structured-streaming-in-synapse-spark/</link>
		
		<dc:creator><![CDATA[RYANJADAMS]]></dc:creator>
		<pubDate>Thu, 12 Jan 2023 23:47:51 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Spark]]></category>
		<category><![CDATA[Synapse]]></category>
		<category><![CDATA[streaming]]></category>
		<guid isPermaLink="false">https://www.ryanjadams.com/?p=9188</guid>

					<description><![CDATA[<p>In this post we are going to look at an example of streaming IoT temperature data in Synapse Spark.&#160; I have an IoT device that will stream temperature data from two sensors to IoT hub. We’ll use Synapse Spark to process the data, and finally write the output to persistent storage. Here is what our [&#8230;]</p>
<p>The post <a href="https://www.ryanjadams.com/2023/01/structured-streaming-in-synapse-spark/">Structured Streaming in Synapse Spark</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In this post we are going to look at an example of streaming IoT temperature data in Synapse Spark.&nbsp; I have an IoT device that will stream temperature data from two sensors to IoT hub. We’ll use Synapse Spark to process the data, and finally write the output to persistent storage. Here is what our architecture will look like:&nbsp;</p>



<figure class="wp-block-image size-large"><a href="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2023/01/Picture1.png?ssl=1"><img data-recalc-dims="1" decoding="async" width="1024" height="373" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2023/01/Picture1.png?resize=1024%2C373&#038;ssl=1" alt="" class="wp-image-9192" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2023/01/Picture1-scaled.png?resize=1024%2C373&amp;ssl=1 1024w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2023/01/Picture1-scaled.png?resize=300%2C109&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2023/01/Picture1-scaled.png?resize=768%2C280&amp;ssl=1 768w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2023/01/Picture1-scaled.png?resize=1536%2C560&amp;ssl=1 1536w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2023/01/Picture1-scaled.png?resize=2048%2C747&amp;ssl=1 2048w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /></a></figure>



<h2 class="wp-block-heading">Prerequisites&nbsp;</h2>



<p class="wp-block-paragraph">Our focus for this post is on streaming the data in Synapse Spark so having a device to send data and the setup of IoT Hub are considered prerequisites. Their setup will not be covered in this article.</p>



<h2 class="wp-block-heading">Step 1 – Create A New Notebook&nbsp;</h2>



<p class="wp-block-paragraph">The first thing you need to do is create a new notebook in Synapse, initialize the variables, and do some setup in the first cell.&nbsp; The setup code is provided below. Go to the <strong>Develop Hub</strong> and click the ellipsis that appears when you hover your cursor over the Notebooks heading.&nbsp; That will display a drop-down menu where you can select “<strong>New notebook</strong>”.&nbsp;</p>



<figure class="wp-block-image size-full"><a href="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2023/01/image.png?ssl=1"><img data-recalc-dims="1" decoding="async" width="422" height="280" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2023/01/image.png?resize=422%2C280&#038;ssl=1" alt="" class="wp-image-9193" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2023/01/image.png?w=422&amp;ssl=1 422w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2023/01/image.png?resize=300%2C199&amp;ssl=1 300w" sizes="auto, (max-width: 422px) 100vw, 422px" /></a></figure>



<h2 class="wp-block-heading">Step 2 – Setup and configuration&nbsp;</h2>



<p class="wp-block-paragraph">The code below has comments to help you fill everything in with the service names and keys of your own services.&nbsp; You need to setup your source by providing the name of your <a href="https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-create-through-portal" target="_blank" rel="noreferrer noopener">IoT</a> or <a href="https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-create" target="_blank" rel="noreferrer noopener">Event Hub</a> along with the connection string and consumer group (if you have one).&nbsp; Your destination will be Data Lake, so you need to supply the container and folder path where you want to land the streaming data.</p>



<p class="wp-block-paragraph">There are two pieces of sensitive information that you do not want to expose in plain text, so you’ll store the key for the storage account and the Event Hub connection string in <a href="https://learn.microsoft.com/en-us/azure/key-vault/general/quick-create-portal" target="_blank" rel="noreferrer noopener">Azure Key Vault</a>.&nbsp; You can easily call these using mssparkutils once you <a href="https://learn.microsoft.com/en-us/azure/synapse-analytics/spark/microsoft-spark-utilities?pivots=programming-language-python%22%20\l%20%22configure-access-to-azure-key-vault" target="_blank" rel="noreferrer noopener">create a linked service to your Key Vault</a>.&nbsp; </p>



<pre class="wp-block-code language-python"><span><code class="hljs language-python"><span class="hljs-comment"># Initialize Variables</span>
storage_account = <span class="hljs-string">'YourStorageAccountName'</span> <span class="hljs-comment"># This is the storage account where we will write out our data stream </span>
event_hub = <span class="hljs-string">'YourEventHubName'</span> <span class="hljs-comment"># This the event hub where we will grab our stream from </span>
consumer_group = <span class="hljs-string">'YourCGNAme'</span> 
<span class="hljs-comment">#This is our event hub consumer group in case we add more consumers later </span>
key_vault = <span class="hljs-string">'YourKeyVault'</span> <span class="hljs-comment">#This is the name of our key valut where we will store our event hub connection string and storage account secret </span>

<span class="hljs-comment"># Setup access to storage account using Key Vault Secret. Last parameter is the secret name </span>
spark.conf.set(<span class="hljs-string">f"fs.azure.account.key.
<span class="hljs-subst">{storage_account}</span>.dfs.core.windows.net"</span>, mssparkutils.credentials.getSecret(key_vault
,<span class="hljs-string">"YourKVSecretName"</span>)) 

<span class="hljs-comment"># Setup storage locations for all data </span>
ROOT_PATH = <span class="hljs-string">f"abfss://YourContainerName@<span class="hljs-subst">{storage_account}</span>.dfs.core.windows.net/"</span> <span class="hljs-comment"># Put your container before the @ sign </span>
BRONZE_PATH = ROOT_PATH + <span class="hljs-string">"bronze/"</span> <span class="hljs-comment">#Folder where we land our raw data stream </span>
CHECKPOINT_PATH = ROOT_PATH + <span class="hljs-string">"checkpoints/"</span> <span class="hljs-comment">#Folder to store our checkpoints in case the stream gets broken </span>

<span class="hljs-comment"># Get Event Hub Connection String from Key Vault </span>
IOT_CS = mssparkutils.credentials.getSecret(key_vault,<span class="hljs-string">'YourHubConnectionStringSecret
Name'</span>) <span class="hljs-comment"># IoT Hub connection string (Event Hub Compatible) </span>
<span class="hljs-comment">#print(IOT_CS) </span>

<span class="hljs-comment"># Setup Event Hub Dictionary with Config Settings </span>
ehConf = {  
  <span class="hljs-string">'eventhubs.connectionString'</span>:sc._jvm.org.apache.spark.eventhubs.EventHubsUtils.encrypt(IOT_CS),   
<span class="hljs-string">'ehName'</span>:event_hub,   
<span class="hljs-string">'eventhubs.consumerGroup'</span>:consumer_group }</code></span></pre>



<h2 class="wp-block-heading">Step 3 &#8211; Start fresh and wipe out ADLS&nbsp;</h2>



<p class="wp-block-paragraph">In case you want to run this action multiple times, you can delete everything in your ADLS container for a fresh start.&nbsp; </p>



<pre class="wp-block-code language-python"><span><code class="hljs language-python"><span class="hljs-comment"># Make sure root path is empty</span>
mssparkutils.fs.rm(ROOT_PATH, <span class="hljs-literal">True</span>) </code></span></pre>



<h2 class="wp-block-heading">Step 4 &#8211; Start streaming the data&nbsp;</h2>



<p class="wp-block-paragraph">Now that you have everything setup and configured it’s time to read the data stream and then write it out to ADLS.&nbsp; First you create a data frame that uses the Event Hub format and our Event Hub Configuration created in Step 1.&nbsp;The next part gets a little tricky because the entire JSON payload of the stream is stored in a single column called “body”.&nbsp; To handle this, we create the schema we intend to use when landing the data in a final data frame.&nbsp; Last, we write the data out to ADLS in parquet format.&nbsp;</p>



<pre class="wp-block-code language-python"><span><code class="hljs language-python"><span class="hljs-keyword">from</span> pyspark.sql.functions <span class="hljs-keyword">import</span> * 
<span class="hljs-keyword">from</span> pyspark.sql.types <span class="hljs-keyword">import</span> * 

<span class="hljs-comment"># Create a DF and load it from the EH stream </span>
df = spark.readStream.format(<span class="hljs-string">"eventhubs"</span>).options(**ehConf).load() 

<span class="hljs-comment"># The stream stores the JSON payload in a single "body" column. </span>
<span class="hljs-comment"># We need a schema to match and extract the JSON </span>
Schema1 = StructType(&#91;StructField(<span class="hljs-string">"temp1"</span>, StringType(), <span class="hljs-literal">True</span>), 
                      StructField(<span class="hljs-string">"temp2"</span>, StringType(), <span class="hljs-literal">True</span>) 
                    ]) 

<span class="hljs-comment"># Extract the JSON and enqueued time </span>
dfRaw = df.select(df.body.cast(<span class="hljs-string">'string'</span>), df.enqueuedTime.alias(<span class="hljs-string">'ArrivalTime'</span>)) 
dfJson = dfRaw.select(from_json(dfRaw.body, schema=Schema1).alias(<span class="hljs-string">"json"</span>), dfRaw.ArrivalTime) 
dfFinal = dfJson.select(<span class="hljs-string">"json.*"</span>, dfJson.ArrivalTime) 

<span class="hljs-comment"># Write the stream to ADLS </span>
<span class="hljs-comment"># We have also enabled checkpoints so if the stream fails we can pick right where we left off </span>

dfFinal.writeStream.format(<span class="hljs-string">"parquet"</span>).outputMode(<span class="hljs-string">"append"</span>).option(<span class="hljs-string">"checkpointLocation"</span>, CHECKPOINT_PATH + <span class="hljs-string">"temperature_raw"</span>).start(BRONZE_PATH + <span class="hljs-string">"temperature_raw"</span>) </code></span></pre>



<h2 class="wp-block-heading">Step 5 &#8211; Read the data streamed to ADLS with Spark Pool&nbsp;</h2>



<p class="wp-block-paragraph">Now that we have the data streaming to ADLS in parquet format, we are going to want to read it and validate the output.&nbsp; You could use the Azure portal or Azure Storage Explorer, but it would be much easier to do it right here using Spark in the same notebook.&nbsp;This part is super easy! We simply configure a few variables for connecting to our ADLS account, read the data into a data frame, and then display the data frame.</p>



<pre class="wp-block-code language-python"><span><code class="hljs language-python"><span class="hljs-keyword">from</span> pyspark.sql <span class="hljs-keyword">import</span> SparkSession 
<span class="hljs-keyword">from</span> pyspark.sql.types <span class="hljs-keyword">import</span> * 

<span class="hljs-comment"># Primary storage info account_name = 'YourStorageAccountName' # fill in your primary account name </span>
container_name = <span class="hljs-string">'YourContainerName'</span> <span class="hljs-comment"># fill in your container name </span>
relative_path = <span class="hljs-string">'bronze/temperature_raw/'</span>  <span class="hljs-comment"># fill in your relative folder path </span>

adls_path = <span class="hljs-string">'abfss://%s@%s.dfs.core.windows.net/%s'</span> % (container_name, account_name, relative_path) 
print(<span class="hljs-string">'Primary storage account path: '</span> + adls_path) 

df_parquet = spark.read.parquet(adls_path) 
display(df_parquet)</code></span></pre>



<h2 class="wp-block-heading">Conclusion&nbsp;</h2>



<p class="wp-block-paragraph">Spark Pools in Azure Synapse support Spark structured streaming so you can stream data right in your Synapse workspace where you can also handle all your other data streams.&nbsp; This makes managing your data estate much easier.&nbsp; You also have the option of four different analytics engines to suit various use-cases or user personas.&nbsp;</p>
<p>The post <a href="https://www.ryanjadams.com/2023/01/structured-streaming-in-synapse-spark/">Structured Streaming in Synapse Spark</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">9188</post-id>	</item>
		<item>
		<title>Always Encrypted Data Displayed in SSRS with a gMSA</title>
		<link>https://www.ryanjadams.com/2020/11/always-encrypted-data-displayed-in-ssrs-with-a-gmsa/</link>
		
		<dc:creator><![CDATA[RYANJADAMS]]></dc:creator>
		<pubDate>Tue, 17 Nov 2020 15:51:00 +0000</pubDate>
				<category><![CDATA[Always Encrypted]]></category>
		<category><![CDATA[Reporting Services]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SQLServerCentral Syndication]]></category>
		<category><![CDATA[Certificate Manager]]></category>
		<category><![CDATA[certmgr]]></category>
		<category><![CDATA[gMSA]]></category>
		<category><![CDATA[Group Managed Service Account]]></category>
		<category><![CDATA[SQL Reporting Services]]></category>
		<category><![CDATA[SSRS]]></category>
		<guid isPermaLink="false">https://www.ryanjadams.com/?p=9008</guid>

					<description><![CDATA[<p>Let&#8217;s take a look at how to display Always Encrypted Data in SSRS with a gMSA (Group Managed Service Account). We are mixing some technologies and the one that throws a wrench into things is the gMSA. Here is the flow for moving the certificates. Always Encrypted protects our data both at rest and in [&#8230;]</p>
<p>The post <a href="https://www.ryanjadams.com/2020/11/always-encrypted-data-displayed-in-ssrs-with-a-gmsa/">Always Encrypted Data Displayed in SSRS with a gMSA</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Let&#8217;s take a look at how to display <a rel="noreferrer noopener" href="https://docs.microsoft.com/en-us/sql/relational-databases/security/encryption/always-encrypted-database-engine" target="_blank">Always Encrypted</a> Data in <a rel="noreferrer noopener" href="https://docs.microsoft.com/en-us/sql/reporting-services/create-deploy-and-manage-mobile-and-paginated-reports" target="_blank">SSRS</a> with a gMSA (<a rel="noreferrer noopener" href="https://docs.microsoft.com/en-us/windows-server/security/group-managed-service-accounts/group-managed-service-accounts-overview" target="_blank">Group Managed Service Account</a>).  We are mixing some technologies and the one that throws a wrench into things is the gMSA.  Here is the flow for moving the certificates.</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" decoding="async" width="1024" height="369" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/AuthFlow.jpg?resize=1024%2C369&#038;ssl=1" alt="Always Encrypted Certificate Source and Destination Example" class="wp-image-9019" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/AuthFlow.jpg?resize=1024%2C369&amp;ssl=1 1024w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/AuthFlow.jpg?resize=300%2C108&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/AuthFlow.jpg?resize=768%2C277&amp;ssl=1 768w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/AuthFlow.jpg?w=1254&amp;ssl=1 1254w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /><figcaption>Always Encrypted Certificate Source and Destination Example</figcaption></figure>



<p class="wp-block-paragraph">Always Encrypted protects our data both at rest and in transit.  To accomplish that, SQL only stores the encrypted data and cannot decrypt it; all the work is done by the client.  In our case the client is SSRS and it is the account running the SSRS service that will need the certificate to decrypt data.  Note that it is not the account running the report.</p>



<h2 class="wp-block-heading">Certificate Stores</h2>



<p class="wp-block-paragraph">When <a rel="noreferrer noopener" href="https://docs.microsoft.com/en-us/sql/relational-databases/security/encryption/always-encrypted-database-engine?view=sql-server-ver15#configuring-always-encrypted" target="_blank">configuring Always Encrypted</a>, we have a choice between two key stores.  In our example we will use the Windows Certificate Store.  You can also use Azure Key Vault, but direct access with that method is <a rel="noreferrer noopener" href="https://docs.microsoft.com/en-us/power-bi/report-server/always-encrypted-support" target="_blank">not supported with SSRS</a>.  You could use the Windows Certificate Store, export the certificate, and import it into Azure Key Vault.  However, SSRS cannot directly access Azure Key Vault.  </p>



<p class="wp-block-paragraph">The certificate is important because we must export it so we can later import it to our clients.  Best practice is to run the Encrypt Column Wizard in SSMS from a server that is not the server hosting your data.  This prevents us from accidentally leaving the cert on the server where if it gets compromised the attacker could get both the data and the certificate.</p>



<h2 class="wp-block-heading">Always Encrypted Wizard</h2>



<p class="wp-block-paragraph">If you run the wizard from the client or application server, you have two choices.  The first is to run SSMS as an administrator so you can store the certificate in the local machine store.  This allows any application on the server to use the certificate if you give it permissions to do so. If you do not run SSMS as an administrator, you will only see the option to store the certificate in the local user store for the current user.</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" decoding="async" width="521" height="333" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image.png?resize=521%2C333&#038;ssl=1" alt="SSMS as Admin Allows Always Encrypted Certificate Storage in Local Machine Store" class="wp-image-9009" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image.png?w=521&amp;ssl=1 521w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image.png?resize=300%2C192&amp;ssl=1 300w" sizes="auto, (max-width: 521px) 100vw, 521px" /><figcaption>SSMS as Admin Allows Always Encrypted Certificate Storage in Local Machine Store</figcaption></figure>



<p class="wp-block-paragraph">For our example we will use the Current User store to comply with the principal of &#8220;<a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Principle_of_least_privilege" target="_blank">Least User Privilege</a>&#8220;.  Once we complete the Always Encrypted wizard, you can open Certificate Manager and export the cert making sure to include the private key.</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" decoding="async" width="664" height="447" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-1.png?resize=664%2C447&#038;ssl=1" alt="Export the Always Encrypted certificate" class="wp-image-9010" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-1.png?w=664&amp;ssl=1 664w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-1.png?resize=300%2C202&amp;ssl=1 300w" sizes="auto, (max-width: 664px) 100vw, 664px" /><figcaption>Export the Always Encrypted certificate</figcaption></figure>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" decoding="async" width="530" height="508" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-2.png?resize=530%2C508&#038;ssl=1" alt="Include the Always Encrypted Certificate Private Key" class="wp-image-9011" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-2.png?w=530&amp;ssl=1 530w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-2.png?resize=300%2C288&amp;ssl=1 300w" sizes="auto, (max-width: 530px) 100vw, 530px" /><figcaption>Include the Always Encrypted Certificate Private Key</figcaption></figure>



<h2 class="wp-block-heading">Always Encrypted Certificate Import</h2>



<p class="wp-block-paragraph">We have our column encrypted and have exported the certificate.  Next, we need to import it on the SSRS server.  This is where the fun begins.  You might be tempted to just import the certificate into the local machine store instead of the user store so all applications can use it.  This does not work, and you will have to import into the user store for the account running the SSRS service.  Normally you could right click the certificate manager and select &#8220;Run As&#8221; to be in the context of the account running SSRS.  This does not work with a gMSA because you don&#8217;t know the password.</p>



<h2 class="wp-block-heading">Get Creative</h2>



<p class="wp-block-paragraph">It&#8217;s time to get creative and break out some <a rel="noreferrer noopener" href="https://docs.microsoft.com/en-us/sysinternals/" target="_blank">SysInternals</a> tools.  We are going to use PSExec to open Certificate Manager under the credentials of our SSRS gMSA account.  We start by opening an administrative command prompt and executing the following from the location where we downloaded the SysInternals tools.</p>



<pre class="wp-block-code language-dos"><span><code class="hljs language-dos">PSExec64.exe -i -u contoso\ssrs_svc$ -p ~ <span class="hljs-built_in">cmd</span>.exe</code></span></pre>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" decoding="async" width="738" height="84" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-3.png?resize=738%2C84&#038;ssl=1" alt="Open Certificate Manager under gMSA" class="wp-image-9012" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-3.png?w=738&amp;ssl=1 738w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-3.png?resize=300%2C34&amp;ssl=1 300w" sizes="auto, (max-width: 738px) 100vw, 738px" /><figcaption>Open Certificate Manager under gMSA to import Always Encrypted Certificate</figcaption></figure>



<p class="wp-block-paragraph">The first parameter -i is needed since we will be launching another command prompt interactively on the desktop. The second parameter -u provides our username and, in our case, the gMSA running the SSRS service.  Remember that gMSA accounts need a $ sign at the end. The third parameter -p is the password for the gMSA account, but we do not know what it is.  The ~ is allowed in place of the password.  The last thing we provide is the executable we want to run.  For us it will be another command prompt.  Ideally, we would like to just run certmgr.exe from here, but that does not work.</p>



<p class="wp-block-paragraph">Once you hit enter another command prompt will open where we can enter certmgr.exe to open the Certificate Manager under the context of our gMSA account.  Now we can finally import the certificate.</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" decoding="async" width="542" height="336" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-4.png?resize=542%2C336&#038;ssl=1" alt="Import your Always Encrypted Certificate" class="wp-image-9013" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-4.png?w=542&amp;ssl=1 542w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-4.png?resize=300%2C186&amp;ssl=1 300w" sizes="auto, (max-width: 542px) 100vw, 542px" /><figcaption>Import your Always Encrypted Certificate</figcaption></figure>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" decoding="async" width="524" height="486" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-5.png?resize=524%2C486&#038;ssl=1" alt="&quot;Include all extended properties&quot; " class="wp-image-9015" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-5.png?w=524&amp;ssl=1 524w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2020/11/image-5.png?resize=300%2C278&amp;ssl=1 300w" sizes="auto, (max-width: 524px) 100vw, 524px" /><figcaption>&#8220;Include all extended properties&#8221; Importing Always Encrypted Certificate</figcaption></figure>



<h2 class="wp-block-heading">View Your Reports</h2>



<p class="wp-block-paragraph">That&#8217;s it!  Now you can go open your report in SSRS and see your Always Encrypted data displayed.  These are some great security technologies, but it takes some outside of the box ideas to get them working together correctly.</p>



<p class="wp-block-paragraph">For more SQL technology intersections check out my post on <a href="https://www.ryanjadams.com/2018/07/sql-server-failover-cluster-instance-azure-msdtc/">Configure SQL Server Failover Cluster Instance On Azure Virtual Machines With MSDTC</a>.</p>
<p>The post <a href="https://www.ryanjadams.com/2020/11/always-encrypted-data-displayed-in-ssrs-with-a-gmsa/">Always Encrypted Data Displayed in SSRS with a gMSA</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">9008</post-id>	</item>
		<item>
		<title>Build a SQL Cluster Lab Part 4</title>
		<link>https://www.ryanjadams.com/2019/11/build-a-sql-cluster-lab-part-4/</link>
					<comments>https://www.ryanjadams.com/2019/11/build-a-sql-cluster-lab-part-4/#comments</comments>
		
		<dc:creator><![CDATA[RYANJADAMS]]></dc:creator>
		<pubDate>Mon, 18 Nov 2019 16:00:49 +0000</pubDate>
				<category><![CDATA[AlwaysOn AG]]></category>
		<category><![CDATA[AlwaysOn FCI]]></category>
		<category><![CDATA[Cluster]]></category>
		<category><![CDATA[Clustering]]></category>
		<category><![CDATA[Disaster Recovery]]></category>
		<category><![CDATA[HyperV]]></category>
		<category><![CDATA[SQLServerCentral Syndication]]></category>
		<category><![CDATA[Virtualization]]></category>
		<category><![CDATA[Windows Server]]></category>
		<category><![CDATA[Availability Group]]></category>
		<category><![CDATA[Failover Cluster]]></category>
		<guid isPermaLink="false">https://www.ryanjadams.com/?p=2269</guid>

					<description><![CDATA[<p>You are going to create a SQL Server Failover Cluster Instance in Part 4 of our series on how to build a SQL Cluster Lab. The FCI will only be installed on Node1 and Node2. FCIs require shared storage so you will make your domain controller an iSCSI target. Last you will create your FCI [&#8230;]</p>
<p>The post <a href="https://www.ryanjadams.com/2019/11/build-a-sql-cluster-lab-part-4/">Build a SQL Cluster Lab Part 4</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">You are going to create a <a rel="noreferrer noopener" aria-label="SQL Server Failover Cluster Instance (opens in a new tab)" href="https://docs.microsoft.com/en-us/sql/sql-server/failover-clusters/windows/always-on-failover-cluster-instances-sql-server" target="_blank">SQL Server Failover Cluster Instance</a> in Part 4 of our series on how to build a SQL Cluster Lab.  The FCI will only be installed on Node1 and Node2.  FCIs require shared storage so you will make your domain controller an iSCSI target.  Last you will create your FCI using the iSCSI drives you presented to the cluster.  Here are links to the other articles in the series. </p>



<ul class="wp-block-list"><li><a rel="noreferrer noopener" aria-label="Part 1 - Building the environment (opens in a new tab)" href="https://www.ryanjadams.com/2019/10/build-a-sql-cluster-lab-part-1/" target="_blank">Part 1 &#8211; Building the environment</a>. Hyper-v VMs, Active Directory, DNS, and Routing and Remote Access Services</li><li><a href="https://www.ryanjadams.com/2019/11/build-a-sql-cluster-lab-part-2/">Part 2 &#8211; Windows Failover Clustering</a>. Cluster validation, networks, and quorum</li><li><a href="https://www.ryanjadams.com/2019/11/build-a-sql-cluster-lab-part-3/">Part 3 &#8211; Availability Group</a>. Dedicated network and AD permissions</li><li><a href="https://www.ryanjadams.com/2019/11/build-a-sql-cluster-lab-part-4/">Part 4 &#8211; Failover Cluster Instance</a>. Creating shared storage using iSCSI and adding the storage to the cluster. </li></ul>



<h2 class="wp-block-heading">Network Configuration</h2>



<p class="wp-block-paragraph">Here is the list of IP addresses from Part 1 that you will need to configure the SQL Server FCI.  </p>



<table class="wp-block-table"><tbody><tr><td></td><td><strong>Data Center 1 IP</strong></td><td><strong>Data Center 2 IP</strong></td></tr><tr><td>Cluster</td><td>10.0.0.10</td><td>172.16.0.10</td></tr><tr><td>AG Listener</td><td>10.0.0.11</td><td>172.16.0.11</td></tr><tr><td>FCI</td><td>10.0.0.15</td><td>N/A</td></tr></tbody></table>



<h2 class="wp-block-heading">Architecture</h2>



<p class="wp-block-paragraph">As a refresher, here is the diagram of the lab you are building. </p>



<figure class="wp-block-image"><a href="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2019/10/Lab-Architecture-1.png?ssl=1" target="_blank" rel="noreferrer noopener"><img data-recalc-dims="1" decoding="async" width="1018" height="395" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2019/10/Lab-Architecture-1.png?resize=1018%2C395&#038;ssl=1" alt="Lab Diagram - Failover Cluster Instance" class="wp-image-2274" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2019/10/Lab-Architecture-1.png?w=1018&amp;ssl=1 1018w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2019/10/Lab-Architecture-1.png?resize=300%2C116&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2019/10/Lab-Architecture-1.png?resize=768%2C298&amp;ssl=1 768w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /></a></figure>



<h2 class="wp-block-heading">Build Failover Cluster Instance Video</h2>



<figure class="wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Build a SQL Cluster Lab Part 4" width="500" height="281" src="https://www.youtube.com/embed/MQSikCe7Vm8?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>
<p>The post <a href="https://www.ryanjadams.com/2019/11/build-a-sql-cluster-lab-part-4/">Build a SQL Cluster Lab Part 4</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.ryanjadams.com/2019/11/build-a-sql-cluster-lab-part-4/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2269</post-id>	</item>
		<item>
		<title>Build a SQL Cluster Lab Part 3</title>
		<link>https://www.ryanjadams.com/2019/11/build-a-sql-cluster-lab-part-3/</link>
		
		<dc:creator><![CDATA[RYANJADAMS]]></dc:creator>
		<pubDate>Mon, 11 Nov 2019 16:00:23 +0000</pubDate>
				<category><![CDATA[AlwaysOn AG]]></category>
		<category><![CDATA[AlwaysOn FCI]]></category>
		<category><![CDATA[Cluster]]></category>
		<category><![CDATA[Clustering]]></category>
		<category><![CDATA[Disaster Recovery]]></category>
		<category><![CDATA[HyperV]]></category>
		<category><![CDATA[SQLServerCentral Syndication]]></category>
		<category><![CDATA[Virtualization]]></category>
		<category><![CDATA[Windows Server]]></category>
		<category><![CDATA[Availability Group]]></category>
		<category><![CDATA[Failover Cluster]]></category>
		<guid isPermaLink="false">https://www.ryanjadams.com/?p=2263</guid>

					<description><![CDATA[<p>You are going to create a multi-subnet Availability Group in Part 3 of our series on how to build a SQL Cluster Lab. First you give the Cluster Name Object rights in Active Directory and then install your AG. You will also ensure the AG endpoints are using the dedicated network created for them in [&#8230;]</p>
<p>The post <a href="https://www.ryanjadams.com/2019/11/build-a-sql-cluster-lab-part-3/">Build a SQL Cluster Lab Part 3</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">You are going to create a multi-subnet <a rel="noreferrer noopener" aria-label="Availability Group (opens in a new tab)" href="https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/overview-of-always-on-availability-groups-sql-server" target="_blank">Availability Group</a> in Part 3 of our series on how to build a SQL Cluster Lab.  First you give the Cluster Name Object rights in Active Directory and then install your AG.  You will also ensure the AG endpoints are using the dedicated network created for them in Part 1.  Here are links to the other articles in the series.</p>



<ul class="wp-block-list"><li><a rel="noreferrer noopener" aria-label="Part 1 - Building the environment (opens in a new tab)" href="https://www.ryanjadams.com/2019/10/build-a-sql-cluster-lab-part-1/" target="_blank">Part 1 &#8211; Building the environment</a>. Hyper-v VMs, Active Directory, DNS, and Routing and Remote Access Services</li><li><a href="https://www.ryanjadams.com/2019/11/build-a-sql-cluster-lab-part-2/">Part 2 &#8211; Windows Failover Clustering</a>. Cluster validation, networks, and quorum</li><li><a href="https://www.ryanjadams.com/2019/11/build-a-sql-cluster-lab-part-3/">Part 3 &#8211; Availability Group</a>. Dedicated network and AD permissions</li><li><a href="https://www.ryanjadams.com/2019/11/build-a-sql-cluster-lab-part-4/">Part 4 &#8211; Failover Cluster Instance</a>. Creating shared storage using iSCSI and adding the storage to the cluster. </li></ul>



<h2 class="wp-block-heading">Create Databases Code</h2>



<pre class="wp-block-code language-sql"><span><code class="hljs language-sql"><span class="hljs-keyword">USE</span> &#91;<span class="hljs-keyword">master</span>];
GO

<span class="hljs-keyword">DROP</span> <span class="hljs-keyword">DATABASE</span> <span class="hljs-keyword">IF</span> <span class="hljs-keyword">EXISTS</span> App1AG_DB1;
GO

<span class="hljs-keyword">DROP</span> <span class="hljs-keyword">DATABASE</span> <span class="hljs-keyword">IF</span> <span class="hljs-keyword">EXISTS</span> App1AG_DB2;
GO

<span class="hljs-keyword">DROP</span> <span class="hljs-keyword">DATABASE</span> <span class="hljs-keyword">IF</span> <span class="hljs-keyword">EXISTS</span> App1AG_DB3;
GO

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">DATABASE</span> &#91;App1AG_DB1]
 <span class="hljs-keyword">ON</span>  PRIMARY 
( <span class="hljs-keyword">NAME</span> = N<span class="hljs-string">'App1AG_DB1'</span>, FILENAME = N<span class="hljs-string">'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\DATA\App1AG_DB1.mdf'</span> , <span class="hljs-keyword">SIZE</span> = <span class="hljs-number">3072</span>KB , <span class="hljs-keyword">MAXSIZE</span> = <span class="hljs-keyword">UNLIMITED</span>, FILEGROWTH = <span class="hljs-number">1024</span>KB )
 <span class="hljs-keyword">LOG</span> <span class="hljs-keyword">ON</span> 
( <span class="hljs-keyword">NAME</span> = N<span class="hljs-string">'App1AG_DB1_log'</span>, FILENAME = N<span class="hljs-string">'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\DATA\App1AG_DB1_log.ldf'</span> , <span class="hljs-keyword">SIZE</span> = <span class="hljs-number">1024</span>KB , <span class="hljs-keyword">MAXSIZE</span> = <span class="hljs-number">2048</span>GB , FILEGROWTH = <span class="hljs-number">1024</span>KB)
<span class="hljs-keyword">GO</span>

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">DATABASE</span> &#91;App1AG_DB2]
 <span class="hljs-keyword">ON</span>  PRIMARY 
( <span class="hljs-keyword">NAME</span> = N<span class="hljs-string">'App1AG_DB2'</span>, FILENAME = N<span class="hljs-string">'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\DATA\App1AG_DB2.mdf'</span> , <span class="hljs-keyword">SIZE</span> = <span class="hljs-number">3072</span>KB , <span class="hljs-keyword">MAXSIZE</span> = <span class="hljs-keyword">UNLIMITED</span>, FILEGROWTH = <span class="hljs-number">1024</span>KB )
 <span class="hljs-keyword">LOG</span> <span class="hljs-keyword">ON</span> 
( <span class="hljs-keyword">NAME</span> = N<span class="hljs-string">'App1AG_DB2_log'</span>, FILENAME = N<span class="hljs-string">'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\DATA\App1AG_DB2_log.ldf'</span> , <span class="hljs-keyword">SIZE</span> = <span class="hljs-number">1024</span>KB , <span class="hljs-keyword">MAXSIZE</span> = <span class="hljs-number">2048</span>GB , FILEGROWTH = <span class="hljs-number">1024</span>KB)
<span class="hljs-keyword">GO</span>

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">DATABASE</span> &#91;App1AG_DB3]
 <span class="hljs-keyword">ON</span>  PRIMARY 
( <span class="hljs-keyword">NAME</span> = N<span class="hljs-string">'App1AG_DB3'</span>, FILENAME = N<span class="hljs-string">'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\DATA\App1AG_DB3.mdf'</span> , <span class="hljs-keyword">SIZE</span> = <span class="hljs-number">3072</span>KB , <span class="hljs-keyword">MAXSIZE</span> = <span class="hljs-keyword">UNLIMITED</span>, FILEGROWTH = <span class="hljs-number">1024</span>KB )
 <span class="hljs-keyword">LOG</span> <span class="hljs-keyword">ON</span> 
( <span class="hljs-keyword">NAME</span> = N<span class="hljs-string">'App1AG_DB3_log'</span>, FILENAME = N<span class="hljs-string">'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\DATA\App1AG_DB3_log.ldf'</span> , <span class="hljs-keyword">SIZE</span> = <span class="hljs-number">1024</span>KB , <span class="hljs-keyword">MAXSIZE</span> = <span class="hljs-number">2048</span>GB , FILEGROWTH = <span class="hljs-number">1024</span>KB)
<span class="hljs-keyword">GO</span>


<span class="hljs-comment">--Full backup required to create an AG</span>
<span class="hljs-keyword">BACKUP</span> <span class="hljs-keyword">DATABASE</span> &#91;App1AG_DB1] <span class="hljs-keyword">TO</span>  DISK = N<span class="hljs-string">'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Backup\App1AG_DB1.bak'</span> <span class="hljs-keyword">WITH</span> <span class="hljs-keyword">FORMAT</span>, INIT,  <span class="hljs-keyword">SKIP</span>, NOREWIND, NOUNLOAD,  STATS = <span class="hljs-number">10</span>, <span class="hljs-keyword">CHECKSUM</span>
<span class="hljs-keyword">GO</span>

<span class="hljs-keyword">BACKUP</span> <span class="hljs-keyword">DATABASE</span> &#91;App1AG_DB2] <span class="hljs-keyword">TO</span>  DISK = N<span class="hljs-string">'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Backup\App1AG_DB2.bak'</span> <span class="hljs-keyword">WITH</span> <span class="hljs-keyword">FORMAT</span>, INIT,  <span class="hljs-keyword">SKIP</span>, NOREWIND, NOUNLOAD,  STATS = <span class="hljs-number">10</span>, <span class="hljs-keyword">CHECKSUM</span>
<span class="hljs-keyword">GO</span>

<span class="hljs-keyword">BACKUP</span> <span class="hljs-keyword">DATABASE</span> &#91;App1AG_DB3] <span class="hljs-keyword">TO</span>  DISK = N<span class="hljs-string">'C:\Program Files\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQL\Backup\App1AG_DB3.bak'</span> <span class="hljs-keyword">WITH</span> <span class="hljs-keyword">FORMAT</span>, INIT,  <span class="hljs-keyword">SKIP</span>, NOREWIND, NOUNLOAD,  STATS = <span class="hljs-number">10</span>, <span class="hljs-keyword">CHECKSUM</span>
<span class="hljs-keyword">GO</span></code></span></pre>



<h2 class="wp-block-heading">Create Availability Group Code</h2>



<pre class="wp-block-code language-sql"><span><code class="hljs language-sql"><span class="hljs-comment">/*
Author: Ryan Adams
Website: https://www.ryanjadams.com/
Twitter: @ryanjadams

This script must be executed in SQLCMD mode.  This script was designed to setup an AlwaysOn Availability Group
in a custom lab environment.  Use at your own risk and DO NOT run this in production.  Make sure you read and understand
it thoroughly.
*/</span>

<span class="hljs-comment">/* First we connect to each replica in order and create logins for the account running the SQL Server Service on the other replicas */</span>
:Connect NODE1

<span class="hljs-keyword">USE</span> &#91;<span class="hljs-keyword">master</span>]
<span class="hljs-keyword">GO</span>

<span class="hljs-keyword">CREATE</span> LOGIN &#91;contoso\svc-sql2] <span class="hljs-keyword">FROM</span> WINDOWS
<span class="hljs-keyword">GO</span>

<span class="hljs-keyword">CREATE</span> LOGIN &#91;contoso\svc-sql3] <span class="hljs-keyword">FROM</span> WINDOWS
<span class="hljs-keyword">GO</span>


:<span class="hljs-keyword">Connect</span> NODE2

<span class="hljs-keyword">USE</span> &#91;<span class="hljs-keyword">master</span>]
<span class="hljs-keyword">GO</span>

<span class="hljs-keyword">CREATE</span> LOGIN &#91;contoso\svc-sql1] <span class="hljs-keyword">FROM</span> WINDOWS
<span class="hljs-keyword">GO</span>

<span class="hljs-keyword">CREATE</span> LOGIN &#91;contoso\svc-sql3] <span class="hljs-keyword">FROM</span> WINDOWS
<span class="hljs-keyword">GO</span>


:<span class="hljs-keyword">Connect</span> NODE3

<span class="hljs-keyword">USE</span> &#91;<span class="hljs-keyword">master</span>];
GO

<span class="hljs-keyword">CREATE</span> LOGIN &#91;contoso\svc-sql1] <span class="hljs-keyword">FROM</span> WINDOWS;
GO

<span class="hljs-keyword">CREATE</span> LOGIN &#91;contoso\svc-sql2] <span class="hljs-keyword">FROM</span> WINDOWS;
GO

<span class="hljs-comment">/* Next we connect to each replica and create the endpoint.
We also grant connect permission on the endpoint to all of the other replica service accounts. */</span>
:Connect NODE1

<span class="hljs-keyword">USE</span> &#91;<span class="hljs-keyword">master</span>];
GO

<span class="hljs-keyword">CREATE</span> ENDPOINT &#91;AlwaysOn_EP]
	STATE = STARTED
	<span class="hljs-keyword">AS</span> TCP (LISTENER_PORT = <span class="hljs-number">5022</span>, LISTENER_IP = (<span class="hljs-number">10.0</span><span class="hljs-number">.1</span><span class="hljs-number">.1</span>)) <span class="hljs-comment">/* Specify an IP value for LISTENER_IP to dedicate AlwaysOn traffic to its own NIC card */</span>
	<span class="hljs-keyword">FOR</span> DATABASE_MIRRORING (<span class="hljs-keyword">ROLE</span> = <span class="hljs-keyword">ALL</span> <span class="hljs-comment">/* Cannot use Witness as that is for mirroring only.  Partner will work but that's really just for mirroring terminology */</span>
		, <span class="hljs-keyword">AUTHENTICATION</span> = WINDOWS NEGOTIATE <span class="hljs-comment">/* Authentication can be Windows or Certificate of a combination in preference order.  Authorization can force NTLM or Kerberos. These are default values */</span>
		, ENCRYPTION = <span class="hljs-keyword">REQUIRED</span> ALGORITHM AES); <span class="hljs-comment">/* Default algorithm is RC4, however RC4 is marked depracated. Use AES (GUI uses AES by default, but TSQL does not) */</span>
GO

<span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">CONNECT</span> <span class="hljs-keyword">ON</span> ENDPOINT::&#91;AlwaysOn_EP] <span class="hljs-keyword">TO</span> &#91;contoso\svc-sql2];
GO

<span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">CONNECT</span> <span class="hljs-keyword">ON</span> ENDPOINT::&#91;AlwaysOn_EP] <span class="hljs-keyword">TO</span> &#91;contoso\svc-sql3];
GO

:Connect NODE2

<span class="hljs-keyword">USE</span> &#91;<span class="hljs-keyword">master</span>];
GO

<span class="hljs-keyword">CREATE</span> ENDPOINT &#91;AlwaysOn_EP]
	STATE = STARTED
	<span class="hljs-keyword">AS</span> TCP (LISTENER_PORT = <span class="hljs-number">5022</span>, LISTENER_IP = (<span class="hljs-number">10.0</span><span class="hljs-number">.1</span><span class="hljs-number">.2</span>)) <span class="hljs-comment">/* Specify an IP value for LISTENER_IP to dedicate AlwaysOn traffic to it's own NIC card */</span>
	<span class="hljs-keyword">FOR</span> DATABASE_MIRRORING (<span class="hljs-keyword">ROLE</span> = <span class="hljs-keyword">ALL</span> <span class="hljs-comment">/* Cannot use Witness as that is for mirroring only.  Partner will work but that's really just for mirroring terminology */</span>
		, <span class="hljs-keyword">AUTHENTICATION</span> = WINDOWS NEGOTIATE <span class="hljs-comment">/* Authentication can be Windows or Certificate of a combination in preference order.  Authorization can force NTLM or Kerberos. These are default values */</span>
		, ENCRYPTION = <span class="hljs-keyword">REQUIRED</span> ALGORITHM AES); <span class="hljs-comment">/* Default algorithm is RC4, however RC4 is marked depracated. Use AES (GUI uses AES by default, but TSQL does not) */</span>
GO

<span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">CONNECT</span> <span class="hljs-keyword">ON</span> ENDPOINT::&#91;AlwaysOn_EP] <span class="hljs-keyword">TO</span> &#91;contoso\svc-sql1];
GO

<span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">CONNECT</span> <span class="hljs-keyword">ON</span> ENDPOINT::&#91;AlwaysOn_EP] <span class="hljs-keyword">TO</span> &#91;contoso\svc-sql3];
GO


:Connect NODE3

<span class="hljs-keyword">USE</span> &#91;<span class="hljs-keyword">master</span>];
GO

<span class="hljs-keyword">CREATE</span> ENDPOINT &#91;AlwaysOn_EP]
	STATE = STARTED
	<span class="hljs-keyword">AS</span> TCP (LISTENER_PORT = <span class="hljs-number">5022</span>, LISTENER_IP = (<span class="hljs-number">172.18</span><span class="hljs-number">.0</span><span class="hljs-number">.3</span>)) <span class="hljs-comment">/* Specify an IP value for LISTENER_IP to dedicate AlwaysOn traffic to it's own NIC card */</span>
	<span class="hljs-keyword">FOR</span> DATABASE_MIRRORING (<span class="hljs-keyword">ROLE</span> = <span class="hljs-keyword">ALL</span> <span class="hljs-comment">/* Cannot use Witness as that is for mirroring only.  Partner will work but that's really just for mirroring terminology */</span>
		, <span class="hljs-keyword">AUTHENTICATION</span> = WINDOWS NEGOTIATE <span class="hljs-comment">/* Authentication can be Windows or Certificate of a combination in preference order.  Authorization can force NTLM or Kerberos. These are default values */</span>
		, ENCRYPTION = <span class="hljs-keyword">REQUIRED</span> ALGORITHM AES); <span class="hljs-comment">/* Default algorithm is RC4, however RC4 is marked depracated. Use AES (GUI uses AES by default, but TSQL does not) */</span>
GO

<span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">CONNECT</span> <span class="hljs-keyword">ON</span> ENDPOINT::&#91;AlwaysOn_EP] <span class="hljs-keyword">TO</span> &#91;contoso\svc-sql1];
GO

<span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">CONNECT</span> <span class="hljs-keyword">ON</span> ENDPOINT::&#91;AlwaysOn_EP] <span class="hljs-keyword">TO</span> &#91;contoso\svc-sql2];
GO

<span class="hljs-comment">/* Here we connect to each replica and ensure that the AlwaysOn extended events session is set to run at startup and that it is also started */</span>
:Connect NODE1

IF EXISTS(<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> sys.server_event_sessions <span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">name</span>=<span class="hljs-string">'AlwaysOn_health'</span>)
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">EVENT</span> <span class="hljs-keyword">SESSION</span> &#91;AlwaysOn_health] <span class="hljs-keyword">ON</span> <span class="hljs-keyword">SERVER</span> <span class="hljs-keyword">WITH</span> (STARTUP_STATE=<span class="hljs-keyword">ON</span>);
<span class="hljs-keyword">END</span>

<span class="hljs-keyword">IF</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span>(<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> sys.dm_xe_sessions <span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">name</span>=<span class="hljs-string">'AlwaysOn_health'</span>)
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">EVENT</span> <span class="hljs-keyword">SESSION</span> &#91;AlwaysOn_health] <span class="hljs-keyword">ON</span> <span class="hljs-keyword">SERVER</span> STATE=<span class="hljs-keyword">START</span>;
<span class="hljs-keyword">END</span>

<span class="hljs-keyword">GO</span>

:<span class="hljs-keyword">Connect</span> NODE2

<span class="hljs-keyword">IF</span> <span class="hljs-keyword">EXISTS</span>(<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> sys.server_event_sessions <span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">name</span>=<span class="hljs-string">'AlwaysOn_health'</span>)
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">EVENT</span> <span class="hljs-keyword">SESSION</span> &#91;AlwaysOn_health] <span class="hljs-keyword">ON</span> <span class="hljs-keyword">SERVER</span> <span class="hljs-keyword">WITH</span> (STARTUP_STATE=<span class="hljs-keyword">ON</span>);
<span class="hljs-keyword">END</span>

<span class="hljs-keyword">IF</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span>(<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> sys.dm_xe_sessions <span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">name</span>=<span class="hljs-string">'AlwaysOn_health'</span>)
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">EVENT</span> <span class="hljs-keyword">SESSION</span> &#91;AlwaysOn_health] <span class="hljs-keyword">ON</span> <span class="hljs-keyword">SERVER</span> STATE=<span class="hljs-keyword">START</span>;
<span class="hljs-keyword">END</span>

<span class="hljs-keyword">GO</span>

:<span class="hljs-keyword">Connect</span> NODE3

<span class="hljs-keyword">IF</span> <span class="hljs-keyword">EXISTS</span>(<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> sys.server_event_sessions <span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">name</span>=<span class="hljs-string">'AlwaysOn_health'</span>)
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">EVENT</span> <span class="hljs-keyword">SESSION</span> &#91;AlwaysOn_health] <span class="hljs-keyword">ON</span> <span class="hljs-keyword">SERVER</span> <span class="hljs-keyword">WITH</span> (STARTUP_STATE=<span class="hljs-keyword">ON</span>);
<span class="hljs-keyword">END</span>

<span class="hljs-keyword">IF</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span>(<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> sys.dm_xe_sessions <span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">name</span>=<span class="hljs-string">'AlwaysOn_health'</span>)
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">EVENT</span> <span class="hljs-keyword">SESSION</span> &#91;AlwaysOn_health] <span class="hljs-keyword">ON</span> <span class="hljs-keyword">SERVER</span> STATE=<span class="hljs-keyword">START</span>;
<span class="hljs-keyword">END</span>

<span class="hljs-keyword">GO</span>

<span class="hljs-comment">/*
There will be 3 databases included in our AG and they currently exist on NODE1.
Prior to SQL 2016 we would have to do the following:
	Backup each database data file to a share and then restore it on each replica making sure to use the "NORECOVERY" clause
	Backup each database log file to a share and then restore it on each replica making sure to use the "NORECOVERY" clause

With SQL 2016+ we can use Direct Seeding instead.
*/</span>

<span class="hljs-comment">/* Here we connect to our primary replica (NODE1) and create our AG. */</span>
:<span class="hljs-keyword">Connect</span> NODE1

<span class="hljs-comment">/* We can use trace flag 9567 to enable compression for the VDI backup for the seeding process */</span>
DBCC TRACEON (<span class="hljs-number">9567</span>, <span class="hljs-number">-1</span>);
GO

<span class="hljs-keyword">USE</span> &#91;<span class="hljs-keyword">master</span>];
GO

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">AVAILABILITY</span> <span class="hljs-keyword">GROUP</span> &#91;App1AG]
<span class="hljs-keyword">WITH</span> (AUTOMATED_BACKUP_PREFERENCE = SECONDARY, FAILURE_CONDITION_LEVEL = <span class="hljs-number">3</span>, HEALTH_CHECK_TIMEOUT = <span class="hljs-number">30000</span>, DB_FAILOVER = <span class="hljs-keyword">ON</span>, DTC_SUPPORT = PER_DB, CLUSTER_TYPE = WSFC) <span class="hljs-comment">/* You can also use SECONDARY_ONLY, PRIMARY, or NONE
Failure condition levels are from the least restrcitive of 1(SQL Service is down) to 5(Exhaustion of worker threads). 3 is the default.
Health check default is 30 seconds, represented in milliseconds. This is how long we wait for sp_server_diagnostics to return. */</span>
<span class="hljs-keyword">FOR</span> <span class="hljs-keyword">DATABASE</span> &#91;App1AG_DB1], &#91;App1AG_DB2], &#91;App1AG_DB3]
REPLICA <span class="hljs-keyword">ON</span> N<span class="hljs-string">'NODE1'</span> <span class="hljs-keyword">WITH</span> (ENDPOINT_URL = N<span class="hljs-string">'TCP://NODE1.contoso.com:5022'</span>, FAILOVER_MODE = <span class="hljs-keyword">AUTOMATIC</span>, AVAILABILITY_MODE = SYNCHRONOUS_COMMIT, BACKUP_PRIORITY = <span class="hljs-number">50</span>, SECONDARY_ROLE(ALLOW_CONNECTIONS = <span class="hljs-keyword">ALL</span>), SESSION_TIMEOUT = <span class="hljs-number">10</span>, SEEDING_MODE = <span class="hljs-keyword">AUTOMATIC</span>),
	N<span class="hljs-string">'NODE2'</span> <span class="hljs-keyword">WITH</span> (ENDPOINT_URL = N<span class="hljs-string">'TCP://NODE2.contoso.com:5022'</span>, FAILOVER_MODE = <span class="hljs-keyword">AUTOMATIC</span>, AVAILABILITY_MODE = SYNCHRONOUS_COMMIT, BACKUP_PRIORITY = <span class="hljs-number">60</span>, SECONDARY_ROLE(ALLOW_CONNECTIONS = <span class="hljs-keyword">ALL</span>), SESSION_TIMEOUT = <span class="hljs-number">10</span>, SEEDING_MODE = <span class="hljs-keyword">AUTOMATIC</span>),
	N<span class="hljs-string">'NODE3'</span> <span class="hljs-keyword">WITH</span> (ENDPOINT_URL = N<span class="hljs-string">'TCP://NODE3.contoso.com:5022'</span>, FAILOVER_MODE = <span class="hljs-keyword">MANUAL</span>, AVAILABILITY_MODE = SYNCHRONOUS_COMMIT, BACKUP_PRIORITY = <span class="hljs-number">70</span>, SECONDARY_ROLE(ALLOW_CONNECTIONS = READ_ONLY), SESSION_TIMEOUT = <span class="hljs-number">10</span>, SEEDING_MODE = <span class="hljs-keyword">AUTOMATIC</span>);
GO

<span class="hljs-comment">/* Here we create a listener for our AG. If you have issues creating the listener check permissions in AD.
	You might also have to turn the AG networks to client and cluster and then turn them back to none post-listener creation*/</span>
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">AVAILABILITY</span> <span class="hljs-keyword">GROUP</span> &#91;App1AG]
<span class="hljs-keyword">ADD</span> LISTENER N<span class="hljs-string">'App1AG'</span> (
<span class="hljs-keyword">WITH</span> IP
((N<span class="hljs-string">'10.0.0.11'</span>, N<span class="hljs-string">'255.255.255.0'</span>),(<span class="hljs-string">'172.16.0.11'</span>,<span class="hljs-string">'255.240.0.0'</span>))
, PORT=<span class="hljs-number">1433</span>);
GO

<span class="hljs-comment">/* Now that the AG exists we connect to each secondary replica and join it to the group
	We also have to grant the CREATE ANY DATABASE permission so Seeding can create the DBs */</span>
:Connect NODE2

<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">AVAILABILITY</span> <span class="hljs-keyword">GROUP</span> &#91;App1AG] <span class="hljs-keyword">JOIN</span>;
GO
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">AVAILABILITY</span> <span class="hljs-keyword">GROUP</span> &#91;App1AG] <span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">ANY</span> <span class="hljs-keyword">DATABASE</span>;
GO

:Connect NODE3

<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">AVAILABILITY</span> <span class="hljs-keyword">GROUP</span> &#91;App1AG] <span class="hljs-keyword">JOIN</span>;
GO
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">AVAILABILITY</span> <span class="hljs-keyword">GROUP</span> &#91;App1AG] <span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">ANY</span> <span class="hljs-keyword">DATABASE</span>;
GO

<span class="hljs-comment">--This query allows us to view seeding performance statistics</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> sys.dm_hadr_physical_seeding_stats;

<span class="hljs-comment">/* Now we need to turn our trace flag back off */</span>
:Connect NODE1
DBCC TRACEOFF (9567, -1);
GO</code></span></pre>



<h2 class="wp-block-heading">Network Configuration</h2>



<p class="wp-block-paragraph">Here is the list of IP addresses from <a href="https://www.ryanjadams.com/2019/10/build-a-sql-cluster-lab-part-1/">Part 1</a> that you will need to configure the AG. </p>



<table class="wp-block-table"><tbody><tr><td></td><td><strong>Data Center 1 IP</strong></td><td><strong>Data Center 2 IP</strong></td></tr><tr><td>Cluster</td><td>10.0.0.10</td><td>172.16.0.10</td></tr><tr><td>AG Listener</td><td>10.0.0.11</td><td>172.16.0.11</td></tr><tr><td>FCI</td><td>10.0.0.15</td><td>N/A</td></tr></tbody></table>



<h2 class="wp-block-heading">Architecture</h2>



<p class="wp-block-paragraph">As a refresher, here is the diagram of the lab you are building. </p>



<figure class="wp-block-image"><a href="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2019/10/Lab-Architecture-1.png?ssl=1" target="_blank" rel="noreferrer noopener"><img data-recalc-dims="1" decoding="async" width="1018" height="395" loading="lazy" src="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2019/10/Lab-Architecture-1.png?resize=1018%2C395&#038;ssl=1" alt="Lab Diagram - Availability Group" class="wp-image-2274" srcset="https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2019/10/Lab-Architecture-1.png?w=1018&amp;ssl=1 1018w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2019/10/Lab-Architecture-1.png?resize=300%2C116&amp;ssl=1 300w, https://i0.wp.com/www.ryanjadams.com/wp-content/uploads/2019/10/Lab-Architecture-1.png?resize=768%2C298&amp;ssl=1 768w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /></a></figure>



<h2 class="wp-block-heading">Build Availability Group Video</h2>



<figure class="wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Build a SQL Cluster Lab Part 3" width="500" height="281" src="https://www.youtube.com/embed/Bn4cSfazwg0?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>
<p>The post <a href="https://www.ryanjadams.com/2019/11/build-a-sql-cluster-lab-part-3/">Build a SQL Cluster Lab Part 3</a> appeared first on <a href="https://www.ryanjadams.com">Ryan Adams Data Blog</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2263</post-id>	</item>
	</channel>
</rss>
