<?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>Scott Klein, Author at SQL Solutions Group</title>
	<atom:link href="https://sqlsolutionsgroup.com/author/scottk/feed/" rel="self" type="application/rss+xml" />
	<link>https://sqlsolutionsgroup.com/author/scottk/</link>
	<description></description>
	<lastBuildDate>Tue, 07 Jul 2026 23:03:07 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.3</generator>

<image>
	<url>https://sqlsolutionsgroup.com/wp-content/uploads/2021/01/cropped-SSG_FAVICON0002-32x32.png</url>
	<title>Scott Klein, Author at SQL Solutions Group</title>
	<link>https://sqlsolutionsgroup.com/author/scottk/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Copying Databases Across SQL Managed Instances</title>
		<link>https://sqlsolutionsgroup.com/copying-databases-across-sql-managed-instances/</link>
					<comments>https://sqlsolutionsgroup.com/copying-databases-across-sql-managed-instances/#comments</comments>
		
		<dc:creator><![CDATA[Scott Klein]]></dc:creator>
		<pubDate>Tue, 23 Apr 2024 11:38:47 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[#microsftcertifedmaster]]></category>
		<category><![CDATA[#microsftpartner]]></category>
		<category><![CDATA[#parallelism #azure]]></category>
		<category><![CDATA[#SQLAB]]></category>
		<category><![CDATA[#SQlatino]]></category>
		<category><![CDATA[#SQlatinoamerica]]></category>
		<category><![CDATA[#sqldatabase]]></category>
		<category><![CDATA[#sqldeveloper]]></category>
		<category><![CDATA[#SQLgroupie]]></category>
		<category><![CDATA[#sqlimer]]></category>
		<category><![CDATA[#sqlimerbymay]]></category>
		<category><![CDATA[#sqlinjection]]></category>
		<category><![CDATA[#sqlinternals]]></category>
		<category><![CDATA[#sqlite]]></category>
		<category><![CDATA[#sqlite3]]></category>
		<category><![CDATA[#SQLLearning]]></category>
		<category><![CDATA[#SQLMagazine]]></category>
		<category><![CDATA[#sqlmanagementstudio]]></category>
		<category><![CDATA[#sqlmanager]]></category>
		<category><![CDATA[#Sqlmap]]></category>
		<category><![CDATA[#sqlrun]]></category>
		<category><![CDATA[#sqlsaturday2017]]></category>
		<category><![CDATA[#sqlsatvienna]]></category>
		<category><![CDATA[#sqlserver]]></category>
		<category><![CDATA[#SQLserver2012]]></category>
		<category><![CDATA[#sqlserver2014]]></category>
		<category><![CDATA[#sqlserver2017]]></category>
		<category><![CDATA[#sqlserver2022]]></category>
		<category><![CDATA[#SQLServeronLinux]]></category>
		<category><![CDATA[#SQLsolutionsgroup]]></category>
		<category><![CDATA[#SQLTraining]]></category>
		<category><![CDATA[#SQLYog]]></category>
		<category><![CDATA[Christmas]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQLPASS]]></category>
		<category><![CDATA[SQLSaturday]]></category>
		<category><![CDATA[SSG]]></category>
		<guid isPermaLink="false">https://sqlsolutionsgroup.com/?p=7024</guid>

					<description><![CDATA[<p>It only seems like yesterday since it was my turn to write a blog. And yet, here we are again, still staring at the M&#38;M dispenser full of dark chocolate peanut M&#38;Ms. (In my last blog I called it a carousel, when in reality, it is more like a Ferris wheel.) Anyway, in this blog [&#8230;]</p>
<p>The post <a href="https://sqlsolutionsgroup.com/copying-databases-across-sql-managed-instances/">Copying Databases Across SQL Managed Instances</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">It only seems like yesterday <a href="https://sqlsolutionsgroup.com/the-right-tool-for-the-right-job/">since it was my turn</a> to write a blog. And yet, here we are again, still staring at the M&amp;M dispenser full of dark chocolate peanut M&amp;Ms. (In my last blog I called it a carousel, when in reality, it is more like a Ferris wheel.)</p>



<p class="wp-block-paragraph">Anyway, in this blog post I want to cover a technology for copying databases across SQL Managed Instances that saved my posterior recently. Well, it didn’t really save it, but it sure made my life easier. We have a client who uses Azure SQL Managed Instance for both their production and test environments. I think this is awesome.</p>



<p class="wp-block-paragraph">As a quick background, Azure SQL Managed Instance is a full SQL Server instance served up as a PaaS service. Unlike Azure SQL Database where you are only working at the database level, Managed Instance gives you near 100% instance compatibility level capabilities with the latest version of SQL Server Enterprise Edition SQL Server database engine. SQL Managed Instance provides the lift and shift capabilities of on-premises databases with minimal database changes.</p>



<p class="wp-block-paragraph">So, back to our customer. They essentially lifted and shifted their on-premises databases to Azure SQL Managed Instance and have been using it successfully for nearly two years. Again, this is awesome.</p>



<p class="wp-block-paragraph">Last week they came to us and asked about reporting with Managed Instance. They were looking at data marts and data warehouses, but we needed more information. It turns out they have some people that just want the ability to query the databases, and potentially hook up Excel to these databases for data analysis.</p>



<p class="wp-block-paragraph">The caveat is that the people I was talking to didn’t want to give the other group direct access to the production environment. <em>Toootally</em> get that. Yeah, like 100% get it. So, what are the options?</p>



<p class="wp-block-paragraph">After some discussion we decided to give the group wanting to run reports access to the test Managed Instance environment. We’ll just refresh the test environment data with the production environment data on a schedule. For them, refreshing data nightly is OK for now.</p>



<p class="wp-block-paragraph">So, the question then becomes, how do you get data, or copy a database, from one SQL Managed Instance to another SQL Managed Instance? There are two ways, and I’ll discuss both here.</p>



<h3 class="wp-block-heading">PowerShell cmdlets</h3>



<p class="wp-block-paragraph">Microsoft released some Powershell cmdlets that make copying data across SQL Managed Instances simple.</p>



<p class="wp-block-paragraph">There are essentially 3 cmdlets:</p>



<ul class="wp-block-list">
<li><strong>Copy-AzSqlInstanceDatabase</strong> – Starts an online copy operation.</li>



<li><strong>Get-AzSqlInstanceDatabaseCopyOperation</strong> – Returns copy operation details.</li>



<li><strong>Complete-AzSqlInstanceDatabaseCopy</strong> – Completes the copy operation.</li>
</ul>



<p class="wp-block-paragraph">The <em>Copy-AzSqlInstancesDatabase</em> cmdlet starts an online copy operation of a database across managed instances using Always On availability group technology. <a href="https://learn.microsoft.com/en-us/powershell/module/az.sql/copy-azsqlinstancedatabase?view=azps-11.4.0">Copy-AzSqlInstanceDatabase (Az.Sql) | Microsoft Learn</a></p>



<p class="wp-block-paragraph">The <em>Get-AzSqlInstanceDatabaseCopyOperation</em> cmdlet returns database copy operation details. You can call this cmdlet in a loop so you can track the status of the copy. <a href="https://learn.microsoft.com/en-us/powershell/module/az.sql/get-azsqlinstancedatabasecopyoperation?view=azps-11.4.0">Get-AzSqlInstanceDatabaseCopyOperation (Az.Sql) | Microsoft Learn</a></p>



<p class="wp-block-paragraph">The <em>Complete-AzSqlInstanceDAtabaseCopy</em> cmdlet completes the copy operation of the database and makes it available for read/write workloads. <a href="https://learn.microsoft.com/en-us/powershell/module/az.sql/complete-azsqlinstancedatabasecopy?view=azps-11.4.0">Complete-AzSqlInstanceDatabaseCopy (Az.Sql) | Microsoft Learn</a></p>



<p class="wp-block-paragraph">You don’t have to call the <em>Get-AzSqlInstanceDatabaseCopyOperation</em> cmdlet as you could just call the Copy and Complete, but it is HIGHLY recommended you call it so you know when to call the complete.</p>



<p class="wp-block-paragraph">The way this works is you start by calling the <em>Copy-AzSqlInstancesDatabase</em> cmdlet. This starts an online copy operation of one or more databases. Once the copy option starts, data is being seeded from the source to the target. During the process, you can call the <em>Get-AzSqlInstanceDatabaseCopyOperation</em> cmdlet to check the copy operation status, either in-progress or completed.</p>



<p class="wp-block-paragraph">Once the copy operation has completed, the status will be copy ready for completion. Within the next 24 hours the user can complete the database copy operation by calling the <em>Get-AzSqlInstanceDatabaseCopyOperation</em> cmdlet. Note that until the <em>Get-AzSqlInstanceDatabaseCopyOperation</em> cmdlet is called, all the changes from the source are being replicated to the target database.</p>



<p class="wp-block-paragraph">When <em>Get-AzSqlInstanceDatabaseCopyOperation</em> is called, the link between the source and target databases is broken, the target database becomes online and ready.</p>



<p class="wp-block-paragraph">A very simple example would be something like this:</p>



<p class="wp-block-paragraph"><div id="wpshdo_1" class="wp-synhighlighter-outer"><div id="wpshdt_1" class="wp-synhighlighter-expanded"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_1"></a><a id="wpshat_1" class="wp-synhighlighter-title" href="#codesyntax_1"  onClick="javascript:wpsh_toggleBlock(1)" title="Click to show/hide code block">Source code</a></td><td align="right"><a href="#codesyntax_1" onClick="javascript:wpsh_code(1)" title="Show code only"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_1" onClick="javascript:wpsh_print(1)" title="Print code"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_1" class="wp-synhighlighter-inner" style="display: block;"><pre class="powershell" style="font-family:monospace;"><span class="re0">$dbname</span> <span class="sy0">=</span> <span class="st0">&quot;db1&quot;</span>
<span class="re0">$sourceMI</span> <span class="sy0">=</span> <span class="st0">&quot;sqlmi1&quot;</span>
<span class="re0">$targetMI</span> <span class="sy0">=</span> <span class="st0">&quot;sqlmi2&quot;</span>
<span class="re0">$sourceRG</span> <span class="sy0">=</span> <span class="st0">&quot;rg1&quot;</span>
<span class="re0">$targetMI</span> <span class="sy0">=</span> <span class="st0">&quot;rg2&quot;</span>
&nbsp;
Copy<span class="sy0">-</span>AzSqlInstanceDatabase `
    <span class="sy0">-</span>DatabaseName <span class="re0">$dbName</span> `
    <span class="sy0">-</span>InstanceName <span class="re0">$sourceMi</span> `
    <span class="sy0">-</span>ResourceGroupName <span class="re0">$sourceRg</span> `
    <span class="sy0">-</span>TargetInstanceName <span class="re0">$targetMi</span> `
    <span class="sy0">-</span>TargetResourceGroupName <span class="re0">$targetRg</span> 
&nbsp;
<span class="kw3">While</span><span class="br0">&#40;</span>true$<span class="br0">&#41;</span>
<span class="br0">&#123;</span>
&nbsp;
    <span class="kw1">Start-Sleep</span> <span class="kw5">-Seconds</span> 10
    <span class="re0">$operation</span> <span class="sy0">=</span> Get<span class="sy0">-</span>AzSqlInstanceDatabaseCopyOperation `
    <span class="sy0">-</span>DatabaseName <span class="re0">$dbname</span> `
    <span class="sy0">-</span>InstanceName <span class="re0">$sourceMI</span> `
    <span class="sy0">-</span>ResourceGroupName <span class="re0">$sourceRG</span> `
    <span class="sy0">-</span>TargetInstanceName <span class="re0">$targetMI</span> `
    <span class="sy0">-</span>TargetResourceGroupName <span class="re0">$targetRG</span>
&nbsp;
    try
    <span class="br0">&#123;</span>
        <span class="re0">$copysucceeded</span> <span class="sy0">=</span> <span class="re0">$true</span>
        <span class="kw1">Write-Host</span> <span class="st0">&quot;Status of copy for &quot;</span> <span class="re0">$dbname</span> <span class="st0">&quot; is &quot;</span> <span class="re0">$operation</span>.State
        <span class="kw3">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="br0">&#40;</span><span class="re0">$operation</span>.State <span class="kw4">-eq</span> <span class="st0">&quot;Succeeded&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><span class="re0">$copysucceeded</span> <span class="sy0">=</span> <span class="re0">$false</span>;<span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
    catch <span class="br0">&#123;</span> <span class="re0">$copysucceeded</span> <span class="sy0">=</span> <span class="re0">$false</span>; <span class="kw1">Write-Host</span> <span class="st0">&quot;Didn't work&quot;</span> <span class="br0">&#125;</span>
&nbsp;
    <span class="kw3">if</span> <span class="br0">&#40;</span><span class="re0">$copysucceeded</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
        <span class="kw1">Write-Host</span> <span class="st0">&quot;Copy completed&quot;</span>
        <span class="kw3">break</span>
    <span class="br0">&#125;</span>
    <span class="kw3">else</span> <span class="br0">&#123;</span> <span class="kw1">Write-Host</span> <span class="st0">&quot;Still copying...&quot;</span> <span class="br0">&#125;</span>
&nbsp;
<span class="br0">&#125;</span>
&nbsp;
Complete<span class="sy0">-</span>AzSqlInstanceDatabaseCopy `
    <span class="sy0">-</span>DatabaseName <span class="re0">$dbName</span> `
    <span class="sy0">-</span>InstanceName <span class="re0">$sourceMI</span> `
    <span class="sy0">-</span>ResourceGroupName <span class="re0">$sourceRG</span> `
    <span class="sy0">-</span>TargetInstanceName <span class="re0">$targetMI</span> `
    <span class="sy0">-</span>TargetResourceGroupName $targetRG</pre></div></div></p>



<p class="wp-block-paragraph">There is a GREAT example here: <a href="https://techcommunity.microsoft.com/t5/azure-sql-blog/how-to-automate-azure-sql-managed-instance-database-copy-and/ba-p/3874505">Using and automating Azure SQL Managed Instance database copy and database move operations with Azure PowerShell (microsoft.com)</a></p>



<p class="wp-block-paragraph">The example copies multiple databases, but if you are needing to copy only one, you can tweak it.</p>



<p class="wp-block-paragraph">Oh, by the way, you can also do the copy via the Azure portal.</p>



<figure class="wp-block-image size-full is-resized"><img fetchpriority="high" decoding="async" width="450" height="228" class="wp-image-7038" style="width: 450px;" src="https://sqlsolutionsgroup.com/wp-content/uploads/2024/04/image.png" alt="" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2024/04/image.png 535w, https://sqlsolutionsgroup.com/wp-content/uploads/2024/04/image-300x152.png 300w" sizes="(max-width: 450px) 100vw, 450px" /></figure>



<h3 class="wp-block-heading">Backup / Restore</h3>



<p class="wp-block-paragraph">The other option is backup and restore, meaning, back up to Azure blob storage then restore from there. Since Managed Instance provides access to the SQL Agent, a job is created on both managed instances; one to back up, and one to restore.</p>



<p class="wp-block-paragraph">This option gets a bit tricky because in the scenario for our client, and I would think in most environments, you want to restore the database in the target managed instance with the same name, and have the backup file the same as well, just to make it easy.</p>



<p class="wp-block-paragraph">The backup job is simple: back up the database to Azure, ensuring the backup file name is always the same name. The restore job is just as simple: delete the database and then restore the database with the same name.</p>



<p class="wp-block-paragraph">However, the issue in this scenario is that when you back up to the same file name, it will error because the file already exists. When we tested we thought, “no problem, we’ll just use PowerShell (Remove-AzureStorageBlob) to remove the file as the first step in the job, then the second step in the job will be to back up the database!”</p>



<p class="wp-block-paragraph">Unfortunately, Managed Instance does not support PowerShell job steps. Sigh. So, we have an on-premises job (or it can even be a Windows Task Scheduler job) that runs the delete. At this point it is just a timing thing. Since the client only wants to back up and restore a single database, we run the backup job at 1 a.m., for example. Then 30 minutes later we run the restore, then another 30 minutes later, we run the delete blob job.</p>
<p>For this solution you’ll need to create a storage account in Azure, then create a CREDENTIAL in SQL Server on both Managed Instances:</p>
<div id="wpshdo_2" class="wp-synhighlighter-outer"><div id="wpshdt_2" class="wp-synhighlighter-expanded"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_2"></a><a id="wpshat_2" class="wp-synhighlighter-title" href="#codesyntax_2"  onClick="javascript:wpsh_toggleBlock(2)" title="Click to show/hide code block">Source code</a></td><td align="right"><a href="#codesyntax_2" onClick="javascript:wpsh_code(2)" title="Show code only"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_2" onClick="javascript:wpsh_print(2)" title="Print code"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_2" class="wp-synhighlighter-inner" style="display: block;"><pre class="powershell" style="font-family:monospace;">CREATE CREDENTIAL <span class="br0">[</span>https:<span class="sy0">//</span>mystorageaccount.blob.core.windows.net<span class="sy0">/</span>mycontainer<span class="br0">]</span>
WITH IDENTITY N<span class="st0">'SHARED ACCESS SIGNATURE'</span><span class="sy0">,</span>
SECRET <span class="sy0">=</span> N<span class="st0">'&lt;storageaccountkey&gt;'</span></pre></div></div>



<p class="wp-block-paragraph">Once that is created, simply back up the database.</p>
<div id="wpshdo_3" class="wp-synhighlighter-outer"><div id="wpshdt_3" class="wp-synhighlighter-expanded"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_3"></a><a id="wpshat_3" class="wp-synhighlighter-title" href="#codesyntax_3"  onClick="javascript:wpsh_toggleBlock(3)" title="Click to show/hide code block">Source code</a></td><td align="right"><a href="#codesyntax_3" onClick="javascript:wpsh_code(3)" title="Show code only"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_3" onClick="javascript:wpsh_print(3)" title="Print code"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_3" class="wp-synhighlighter-inner" style="display: block;"><pre class="powershell" style="font-family:monospace;">BACKUP DATABASE <span class="br0">[</span>MyDB<span class="br0">]</span> TO URL <span class="sy0">=</span> N<span class="st0">'https://mystorageaccount.blob.core.windows.net/mycontainer/MyDB.bak'</span>
	WITH BLOCKSIZE <span class="sy0">=</span> <span class="nu0">65535</span><span class="sy0">,</span> COPY_ONLY<span class="sy0">,</span> NOFORMAT<span class="sy0">,</span> NOINIT<span class="sy0">,</span> 
	NAME <span class="sy0">=</span> N<span class="st0">'MyDB-Full Database Backup'</span><span class="sy0">,</span> NOSKIP<span class="sy0">,</span> NOREWIND<span class="sy0">,</span> NOUNLOAD<span class="sy0">,</span> STATS <span class="sy0">=</span> <span class="nu0">10</span></pre></div></div>
<p>Or, if you are using Ola Hallengren’s solution (which we are a major fan of):</p>
<div id="wpshdo_4" class="wp-synhighlighter-outer"><div id="wpshdt_4" class="wp-synhighlighter-expanded"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_4"></a><a id="wpshat_4" class="wp-synhighlighter-title" href="#codesyntax_4"  onClick="javascript:wpsh_toggleBlock(4)" title="Click to show/hide code block">Source code</a></td><td align="right"><a href="#codesyntax_4" onClick="javascript:wpsh_code(4)" title="Show code only"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_4" onClick="javascript:wpsh_print(4)" title="Print code"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_4" class="wp-synhighlighter-inner" style="display: block;"><pre class="powershell" style="font-family:monospace;">EXECUTE <span class="br0">[</span>dbo<span class="br0">]</span>.<span class="br0">[</span>DatabaseBackup<span class="br0">]</span>
<span class="sy0">@</span>Databases <span class="sy0">=</span> <span class="st0">'SentryOne'</span><span class="sy0">,</span>
<span class="sy0">@</span>URL <span class="sy0">=</span> <span class="st0">'https://mystorageaccount.blob.core.windows.net/mycontainer'</span><span class="sy0">,</span>
<span class="sy0">@</span>Credential <span class="sy0">=</span> <span class="st0">'https://mystorageaccount.blob.core.windows.net/mycontainer'</span><span class="sy0">,</span>
<span class="sy0">@</span>BackupType <span class="sy0">=</span> <span class="st0">'FULL'</span><span class="sy0">,</span>
<span class="sy0">@</span>Verify <span class="sy0">=</span> <span class="st0">'N'</span><span class="sy0">,</span>
<span class="sy0">@</span>CheckSum <span class="sy0">=</span> <span class="st0">'Y'</span><span class="sy0">,</span>
<span class="sy0">@</span>LogToTable <span class="sy0">=</span> <span class="st0">'Y'</span><span class="sy0">,</span>
<span class="sy0">@</span>Compress <span class="sy0">=</span> <span class="st0">'Y'</span><span class="sy0">,</span>
<span class="sy0">@</span>CopyOnly <span class="sy0">=</span> <span class="st0">'Y'</span></pre></div></div>
<p>Then restore onto the other Managed Instance:</p>
<div id="wpshdo_5" class="wp-synhighlighter-outer"><div id="wpshdt_5" class="wp-synhighlighter-expanded"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_5"></a><a id="wpshat_5" class="wp-synhighlighter-title" href="#codesyntax_5"  onClick="javascript:wpsh_toggleBlock(5)" title="Click to show/hide code block">Source code</a></td><td align="right"><a href="#codesyntax_5" onClick="javascript:wpsh_code(5)" title="Show code only"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_5" onClick="javascript:wpsh_print(5)" title="Print code"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_5" class="wp-synhighlighter-inner" style="display: block;"><pre class="powershell" style="font-family:monospace;">RESTORE DATABASE <span class="br0">[</span>MyDB<span class="br0">]</span> FROM URL <span class="sy0">=</span> N<span class="st0">'https://mystorageaccount.blob.core.windows.net/mycontainer/MyDB.bak'</span></pre></div></div>
<p>Yeah, a bit cumbersome, but it works and runs, and solves the client’s problem.</p>



<h3 class="wp-block-heading">Which?</h3>



<p class="wp-block-paragraph">As in all things SQL, it depends. For our client, the simple backup and restore was the solution. They were OK with data being up to 24 hours old for their reporting in this scenario.</p>



<p class="wp-block-paragraph">If using the PowerShell cmdlets, the target database is unavailable for connections until you issue the Complete cmdlet. But it lets you copy multiple databases, not just one.</p>



<p class="wp-block-paragraph">It really just comes down to what you are attempting to do. Honestly, for the customer, we could have easily used the PowerShell cmdlets to copy the database each night (deleting the database on the target Managed Instance before starting the copy) and that would eliminate the need to delete the file from blob storage.</p>



<p class="wp-block-paragraph">So if you looking for ways of copying databases across SQL Managed Instances, you have several options.</p>



<p class="wp-block-paragraph">&nbsp;</p>
<p>The post <a href="https://sqlsolutionsgroup.com/copying-databases-across-sql-managed-instances/">Copying Databases Across SQL Managed Instances</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlsolutionsgroup.com/copying-databases-across-sql-managed-instances/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>The Right Tool for the Right Job</title>
		<link>https://sqlsolutionsgroup.com/the-right-tool-for-the-right-job/</link>
					<comments>https://sqlsolutionsgroup.com/the-right-tool-for-the-right-job/#comments</comments>
		
		<dc:creator><![CDATA[Scott Klein]]></dc:creator>
		<pubDate>Mon, 15 Jan 2024 12:42:03 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[#microsftcertifedmaster]]></category>
		<category><![CDATA[#microsftpartner]]></category>
		<category><![CDATA[#parallelism #azure]]></category>
		<category><![CDATA[#SQLAB]]></category>
		<category><![CDATA[#SQlatino]]></category>
		<category><![CDATA[#SQlatinoamerica]]></category>
		<category><![CDATA[#sqldatabase]]></category>
		<category><![CDATA[#sqldeveloper]]></category>
		<category><![CDATA[#SQLgroupie]]></category>
		<category><![CDATA[#sqlimer]]></category>
		<category><![CDATA[#sqlimerbymay]]></category>
		<category><![CDATA[#sqlinjection]]></category>
		<category><![CDATA[#sqlinternals]]></category>
		<category><![CDATA[#sqlite]]></category>
		<category><![CDATA[#sqlite3]]></category>
		<category><![CDATA[#SQLLearning]]></category>
		<category><![CDATA[#SQLMagazine]]></category>
		<category><![CDATA[#sqlmanagementstudio]]></category>
		<category><![CDATA[#sqlmanager]]></category>
		<category><![CDATA[#Sqlmap]]></category>
		<category><![CDATA[#sqlrun]]></category>
		<category><![CDATA[#sqlsaturday2017]]></category>
		<category><![CDATA[#sqlsatvienna]]></category>
		<category><![CDATA[#sqlserver]]></category>
		<category><![CDATA[#SQLserver2012]]></category>
		<category><![CDATA[#sqlserver2014]]></category>
		<category><![CDATA[#sqlserver2017]]></category>
		<category><![CDATA[#sqlserver2022]]></category>
		<category><![CDATA[#SQLServeronLinux]]></category>
		<category><![CDATA[#SQLsolutionsgroup]]></category>
		<category><![CDATA[#SQLTraining]]></category>
		<category><![CDATA[#SQLYog]]></category>
		<category><![CDATA[Christmas]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQLPASS]]></category>
		<category><![CDATA[SQLSaturday]]></category>
		<category><![CDATA[SSG]]></category>
		<guid isPermaLink="false">https://sqlsolutionsgroup.com/?p=7000</guid>

					<description><![CDATA[<p>I’m looking forward to 2024. From many perspectives, I think we are going to see great advancements, including in technology, that on one hand will make our lives easier, but also make our decisions a bit harder.</p>
<p>The post <a href="https://sqlsolutionsgroup.com/the-right-tool-for-the-right-job/">The Right Tool for the Right Job</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><em><strong>Reminiscing</strong></em></p>
<p>We’re two weeks into 2024 and I’m sitting here staring at my carousel M&amp;M dispenser (it used to be full) of dark chocolate peanut M&amp;Ms. Some people collect hats, stamps, or Lego sets. I collect M&amp;M dispensers. I probably have 50 of them (and the dark chocolate peanut M&amp;M is the ONLY way to go, btw), and I&#8217;m wondering what 2024 will bring.</p>
<p><img decoding="async" class="alignnone wp-image-7001" src="https://sqlsolutionsgroup.com/wp-content/uploads/2024/01/a-toy-machine-and-a-microphone-on-a-table-descrip.png" alt="A toy machine and a microphone on a table

Description automatically generated" width="650" height="405" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2024/01/a-toy-machine-and-a-microphone-on-a-table-descrip.png 1201w, https://sqlsolutionsgroup.com/wp-content/uploads/2024/01/a-toy-machine-and-a-microphone-on-a-table-descrip-300x187.png 300w, https://sqlsolutionsgroup.com/wp-content/uploads/2024/01/a-toy-machine-and-a-microphone-on-a-table-descrip-1024x638.png 1024w, https://sqlsolutionsgroup.com/wp-content/uploads/2024/01/a-toy-machine-and-a-microphone-on-a-table-descrip-768x478.png 768w" sizes="(max-width: 650px) 100vw, 650px" /></p>
<p>And yes, that is a mic propped up on a mini Starbucks cup because my laptop mic sounds like crap. So I hooked that thing up, but it lacks a stand, so I’ll be ordering one of those.</p>
<p>Anyway, back to 2024.</p>
<p>I don’t want to turn this blog into a “look back at 2023” or “what could have been” post. I want to look forward. I mean, it’s 2024 and, barely two weeks in, a lot has happened. Microsoft has already released CU 11 for SQL Server 2022. Pete Carroll and Bill Belichick (two GREAT NFL coaches) were let go, and Nick Saban retired (a phenomenal college coach), all in the span of 24 hours.</p>
<p>Plus, for the past few days my laptop has been popping up messages that my Norton antivirus software has expired (and others), and I am not even running it. Looks like I’ll be paving my laptop this weekend. Ugh, welcome to 2024.</p>
<p>But honestly, I’m looking forward to 2024. From many perspectives, I think we are going to see great advancements, including in technology, that on one hand will make our lives easier, but also make our decisions a bit harder. My dad died last July, a week before his 90th birthday, and for the few months following, my bothers and I helped Mom clean and organize a lot of my dad’s things.</p>
<p>He had a lot of tools. <em><strong>A lot.</strong></em> It took us days just to go through his tools. I brought some back home with me. My dad had a really nice snow blower, because where he lived it snowed quite a bit, and he used it to clear the pathways of not only his house, but his neighbors&#8217;. He enjoyed bundling up and firing up his trusty mega snowblower 9000 (no, it’s not really called that), making mounds and piles of snow by clearing the sidewalks and walkways of his neighbors.</p>
<p>Where I live, it doesn’t snow that much, so I’m torn. I’d love for it to snow enough to justify firing up the snow blower, but I also DON’T want it to snow that much. When it does snow (like it did the other night), I’m looking at my inherited snow blower and snow shovels and wondering what the best tool for the job would be. My neighbors would probably get a kick out of seeing me fire that thing up for only a 1/8 of an inch of snow, which, if I just waited a couple of hours, would be gone anyway. *Sigh*</p>
<p>So, here I sit, two weeks into 2024, pushing the button on my carousel M&amp;M dispenser waiting for it to drop out those precious dark chocolate peanut M&amp;M’s, thinking about the upcoming projects and the technology available to me.</p>
<p>Do I use SSIS or Azure Data Factory? Would a particular client benefit from a distributed availability group? Why isn’t Query Store enabled at this other client? What about AI (have you seen the movies <em>M3GAN</em> and <em>Moonfall?</em>. Um, no.) I’m kidding a bit, because I just read a great blog post by Daniel Janik on <a href="https://sqltechblog.com/2024/01/08/using-ai-to-create-test-data/">how to use AI to generate test data</a> in SQL Server.</p>
<p>Anyway, make 2024 a great one. Help others, be kind, play with awesome technology, and eat a bunch of dark chocolate peanut M&amp;Ms (preferably out of a carousel dispenser).</p>
<p>The post <a href="https://sqlsolutionsgroup.com/the-right-tool-for-the-right-job/">The Right Tool for the Right Job</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlsolutionsgroup.com/the-right-tool-for-the-right-job/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>PolyBase vs Linked Servers </title>
		<link>https://sqlsolutionsgroup.com/polybase-vs-linked-servers/</link>
		
		<dc:creator><![CDATA[Scott Klein]]></dc:creator>
		<pubDate>Thu, 19 Oct 2023 16:15:12 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[#microsftcertifedmaster]]></category>
		<category><![CDATA[#microsftpartner]]></category>
		<category><![CDATA[#parallelism #azure]]></category>
		<category><![CDATA[#SQLAB]]></category>
		<category><![CDATA[#SQlatino]]></category>
		<category><![CDATA[#SQlatinoamerica]]></category>
		<category><![CDATA[#sqldatabase]]></category>
		<category><![CDATA[#sqldeveloper]]></category>
		<category><![CDATA[#SQLgroupie]]></category>
		<category><![CDATA[#sqlimer]]></category>
		<category><![CDATA[#sqlimerbymay]]></category>
		<category><![CDATA[#sqlinjection]]></category>
		<category><![CDATA[#sqlinternals]]></category>
		<category><![CDATA[#sqlite]]></category>
		<category><![CDATA[#sqlite3]]></category>
		<category><![CDATA[#SQLLearning]]></category>
		<category><![CDATA[#SQLMagazine]]></category>
		<category><![CDATA[#sqlmanagementstudio]]></category>
		<category><![CDATA[#sqlmanager]]></category>
		<category><![CDATA[#Sqlmap]]></category>
		<category><![CDATA[#sqlrun]]></category>
		<category><![CDATA[#sqlsaturday2017]]></category>
		<category><![CDATA[#sqlsatvienna]]></category>
		<category><![CDATA[#sqlserver]]></category>
		<category><![CDATA[#SQLserver2012]]></category>
		<category><![CDATA[#sqlserver2014]]></category>
		<category><![CDATA[#sqlserver2017]]></category>
		<category><![CDATA[#sqlserver2022]]></category>
		<category><![CDATA[#SQLServeronLinux]]></category>
		<category><![CDATA[#SQLsolutionsgroup]]></category>
		<category><![CDATA[#SQLTraining]]></category>
		<category><![CDATA[#SQLYog]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQLPASS]]></category>
		<category><![CDATA[SQLSaturday]]></category>
		<category><![CDATA[SSG]]></category>
		<guid isPermaLink="false">https://sqlsolutionsgroup.com/?p=6951</guid>

					<description><![CDATA[<p>PolyBase vs. Linked Servers &#8230; which wins? Recently we have had several clients reach out to us about a data archival solution. Some of their databases are in the TB range, with data going back 7-10 years or even more. Keeping this data in the database is costly due to disk space needs, and query [&#8230;]</p>
<p>The post <a href="https://sqlsolutionsgroup.com/polybase-vs-linked-servers/">PolyBase vs Linked Servers </a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>PolyBase vs. Linked Servers &#8230; which wins? Recently we have had several clients reach out to us about a data archival solution. Some of their databases are in the TB range, with data going back 7-10 years or even more. Keeping this data in the database is costly due to disk space needs, and query performance can be affected due to the amount of data being queried as well as index sizes, etc.</p>
<p>Several of our clients have looked at a range of different solutions, such as copying “old” data to a different report or read-only server and hooking up a Linked Server through which to query the older data when needed. Another option is creating a “history” table in the same database and moving the older data to that table.</p>
<p>In both cases, this doesn’t solve the disk space issue, and it is still a costly storage solution. With the first scenario, I’m sure we have all been a little frustrated at the performance of Linked Servers at one time or another. In all cases, we had a requirements-gathering session with each client to determine several items, including:</p>
<ul>
<li>Does the archive data need to be queried, and if so, how often?</li>
<li>How much data needs to be archived?</li>
<li>Does referential integrity need to be kept between current and archive data?</li>
<li>Is cloud storage an option?</li>
</ul>
<p>There are other questions that we could ask, but these are the most critical because it establishes a baseline and determines the technology we can work with. In some cases, cloud storage is not an option, so we needed to stick with an on-premises solution. Where cloud storage was an option, that opened things up and we were able to look at and utilize some great technologies.</p>
<p>Now, where cloud storage wasn’t an option, there were still some things that we could look at that are strong, viable options, thus the topic of this blog. With SQL Server 2016, Microsoft added the ability to use PolyBase to query other SQL Server instances. This is a game changer, as PolyBase is very powerful. We at SSG set out to determine which is faster and more efficient, PolyBase or Linked Servers, when querying one SQL Server instance from another SQL Server instance.</p>
<p>Honestly, what we found was very surprising. <strong><em>Extremely surprising</em></strong>. Granted, all our tests were performed using SQL Server 2019 and SQL Server 2022, but even then, what we found was quite shocking.</p>
<p>I could walk through everything here, which would make for an extremely long blog, so I did a webinar on this exact topic with a lot of demos, discussion, and insights, and you can view it <a href="https://www.youtube.com/watch?v=l9Rc5nMuq48" target="_blank" rel="noopener">here</a>. Let us know what you think as we always like to hear from you.</p>
<p>The post <a href="https://sqlsolutionsgroup.com/polybase-vs-linked-servers/">PolyBase vs Linked Servers </a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Index Maintenance: Automating Azure with Elastic Jobs</title>
		<link>https://sqlsolutionsgroup.com/index-maintenance-automating-azure-with-elastic-jobs/</link>
					<comments>https://sqlsolutionsgroup.com/index-maintenance-automating-azure-with-elastic-jobs/#comments</comments>
		
		<dc:creator><![CDATA[Scott Klein]]></dc:creator>
		<pubDate>Mon, 21 Aug 2023 23:17:40 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[#microsftcertifedmaster]]></category>
		<category><![CDATA[#microsftpartner]]></category>
		<category><![CDATA[#parallelism #azure]]></category>
		<category><![CDATA[#SQLAB]]></category>
		<category><![CDATA[#SQlatino]]></category>
		<category><![CDATA[#SQlatinoamerica]]></category>
		<category><![CDATA[#sqldatabase]]></category>
		<category><![CDATA[#sqldeveloper]]></category>
		<category><![CDATA[#SQLgroupie]]></category>
		<category><![CDATA[#sqlimer]]></category>
		<category><![CDATA[#sqlimerbymay]]></category>
		<category><![CDATA[#sqlinjection]]></category>
		<category><![CDATA[#sqlinternals]]></category>
		<category><![CDATA[#sqlite]]></category>
		<category><![CDATA[#sqlite3]]></category>
		<category><![CDATA[#SQLLearning]]></category>
		<category><![CDATA[#SQLMagazine]]></category>
		<category><![CDATA[#sqlmanagementstudio]]></category>
		<category><![CDATA[#sqlmanager]]></category>
		<category><![CDATA[#Sqlmap]]></category>
		<category><![CDATA[#sqlrun]]></category>
		<category><![CDATA[#sqlsaturday2017]]></category>
		<category><![CDATA[#sqlsatvienna]]></category>
		<category><![CDATA[#sqlserver]]></category>
		<category><![CDATA[#SQLserver2012]]></category>
		<category><![CDATA[#sqlserver2014]]></category>
		<category><![CDATA[#sqlserver2017]]></category>
		<category><![CDATA[#sqlserver2022]]></category>
		<category><![CDATA[#SQLServeronLinux]]></category>
		<category><![CDATA[#SQLsolutionsgroup]]></category>
		<category><![CDATA[#SQLTraining]]></category>
		<category><![CDATA[#SQLYog]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQLPASS]]></category>
		<category><![CDATA[SQLSaturday]]></category>
		<category><![CDATA[SSG]]></category>
		<guid isPermaLink="false">https://sqlsolutionsgroup.com/?p=6926</guid>

					<description><![CDATA[<p>We often see companies not doing index maintenance in Azure. This will get you started because Index Maintenance with Elastic Jobs is easy.</p>
<p>The post <a href="https://sqlsolutionsgroup.com/index-maintenance-automating-azure-with-elastic-jobs/">Index Maintenance: Automating Azure with Elastic Jobs</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">It&#8217;s finally here: the third and final blog post about Azure automation. The first blog covered&nbsp;<a href="https://sqlsolutionsgroup.com/azure-automation-and-runbooks/">how to automate Azure using Runbooks</a>, the second blog post showed&nbsp;<a href="https://sqlsolutionsgroup.com/how-to-running-sql-jobs-with-azure-functions/">how to do it using Azure Functions</a>, and this blog post will cover how to do it using Azure Elastic jobs.</p>



<p class="wp-block-paragraph">To be fair, I titled this blog “Automating Azure with Elastic Jobs”, but Elastic Jobs isn’t part of Azure Automation, so please don’t get confused. The goal with this is to demonstrate how to automate some Azure database DBA tasks.</p>



<h3 class="wp-block-heading">What are Elastic Jobs?</h3>



<p class="wp-block-paragraph">Like my two previous blog posts and their respective topics, this blog isn’t a deep dive into Elastic Jobs. This isn’t an “Intro to Elastic Jobs” blog post. That is at least one or more blogs itself, and the purpose of this blog isn’t to dive deep into Elastic Jobs. Additionally, Elastic Jobs is in Preview, which means&nbsp;<em><strong>things may change</strong></em>.</p>



<p class="wp-block-paragraph">Elastic Jobs allows you to run T-SQL scripts across multiple databases, in parallel. Yeah, totally cool. So for us at SSG, we have multiple clients that have their database environment in Azure, and they came to us asking how to do index maintenance. Well,&nbsp;<em><strong>HERE YA GO!</strong></em></p>



<p class="wp-block-paragraph">As I stated in my previous two blogs, those solutions work just as well. I&#8217;ve given you some options, and one solution might fit your environment better than the others. In fact, we have several clients using Runbooks or Azure Functions.</p>



<p class="wp-block-paragraph">The way Elastic Jobs works is that you create jobs that are executed against one or more Azure SQL databases, and those jobs run T-SQL queries to perform the defined tasks. As part of setting up the Elastic Job, you define a set of target databases against which the job or jobs will be executed. Additionally, you can define schedules for those jobs. Pretty sweet.</p>



<p class="wp-block-paragraph">So, with that foundation, let’s build an Elastic Job to optimize indexes.</p>



<h3 class="wp-block-heading">Getting Started</h3>



<p class="wp-block-paragraph">Before we get started, I should mention there is a REALLY sweet demo of this on Microsoft Learn&nbsp;<a href="https://learn.microsoft.com/en-us/azure/azure-sql/database/elastic-jobs-powershell-create?view=azuresql">here</a>. So why am I covering it in this blog? Simple. The example on Microsoft Learn creates EVERYTHNG needed for the demo. And I mean EVERYTHING, including databases, resource groups, the whole 9 yards. It is a great way to get started with Elastic Jobs if you are new to it. I ran the demo just to get familiar with it. So if you want to just learn about Elastic Jobs, stop reading, click the link above, and enjoy.</p>



<p class="wp-block-paragraph">However, in my scenario, I already had the databases, resource groups, and some of the other objects, so I needed to tweak the demo to fit my needs. Essentially, this tells the script “hey, I already have some of these things, so don’t do x and y”.</p>



<p class="wp-block-paragraph">To get started, we need a few things:</p>



<ol class="wp-block-list">
<li>A couple of Azure SQL databases</li>



<li>Ola Hallengren’s index optimize script</li>



<li>An Azure Elastic Job Agent</li>



<li>The Az PowerShell module</li>
</ol>



<h3 class="wp-block-heading">Create the Databases</h3>



<p class="wp-block-paragraph">For this blog, I simply created two new General Purpose databases, based off of the Sample database.</p>



<figure class="wp-block-image"><img decoding="async" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/08/a-screenshot-of-a-computer-screen-description-aut.png" alt="A screenshot of a computer screen Description automatically generated"/></figure>



<h3 class="wp-block-heading">Run the Scripts</h3>



<p class="wp-block-paragraph">With the databases created, the next step is to add the appropriate components to the database to do the index optimization. We at SSG are huge fans of Ola Hallengren and his Maintenance Solution, and you can get his awesome scripts&nbsp;<a href="https://ola.hallengren.com/downloads.html">here</a>.</p>



<p class="wp-block-paragraph">The IndexOptimize.sql script will run in Azure, but you will also need to download the CommandLog.sql and CommandExecute.sql files as well. Run all three of those in the database you just created. Start with the CommandExecute.sql, then the CommmandLog.sql, then the IndexOptimize.sql.</p>



<h3 class="wp-block-heading">Create the Elastic Job Agent</h3>



<p class="wp-block-paragraph">With everything prepared, the next step is to create the Elastic Job agent. In the Azure Portal, create a new resource, and in the search box type “elastic job” in the search bar and select elastic job agent.</p>



<figure class="wp-block-image"><img decoding="async" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/08/a-blue-and-white-box-with-black-text-description.png" alt="A blue and white box with black text Description automatically generated"/></figure>



<p class="wp-block-paragraph">Click&nbsp;<strong>Create</strong>.</p>



<figure class="wp-block-image"><img decoding="async" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/08/a-screenshot-of-a-computer-description-automatica.png" alt="A screenshot of a computer Description automatically generated"/></figure>



<p class="wp-block-paragraph">It is pretty straight forward; give the agent a name and select the same subscription in which you created the sample databases, then click Review + Create.</p>



<h3 class="wp-block-heading">PowerShell</h3>



<p class="wp-block-paragraph">If you read the Microsoft Learn doc I linked to above, it shows how to create the appropriate objects using PowerShell. There is a similar tutorial which does everything in T-SQL. This blog will use PowerShell, but you can do the same thing in T-SQL.</p>



<p class="wp-block-paragraph">For what I needed, I copied everything from the doc into PowerShell, then started hacking. I cut what I didn’t need, modified some things, and tested and tested until it worked the way I wanted it. The code below represents the final results.</p>



<p class="wp-block-paragraph">This code assumes you already have a resource group, Azure SQL Server, and some other things. It does create the JobDatabase database, which I am OK with, but other than that, it just creates all the Elastic Job stuff. As an FYI, the job database is the database the job agent uses to store job related data, job definitions, etc. It is recommended that this database be a clean, empty database, at an S1 service level or higher. Thus, my code below creates a brand-new database for this.</p>



<p class="wp-block-paragraph">In this example, I create a group, which by default adds all the databases on the server, but then I tell it to EXCLUDE the jobs database. You can see that I have lines of code which are commented out that add the two sample databases. I could not get those two lines of code to work for the life of me, and I’ll attribute that to the fact that this is in preview. The only way I could get it to work was to create the group but exclude the jobs database.</p>



<p class="wp-block-paragraph">At the very end, the code creates a job, then creates a schedule for that job. The job creation specifies the T-SQL to run, which is the index optimize stored procedure. There is also commented code at the end which will start a new execution of the job if you would like, otherwise it will kick off at the scheduled time.</p>



<p class="wp-block-paragraph"><b>NOTE</b>: As noted earlier, you will need the Az PowerShell module. An important note here is that the very first line imports the Az module, so if you haven’t previously installed this PowerShell module, you will need to do so by executing&nbsp;<i>Install-Module Az</i>.</p>


<div id="wpshdo_6" class="wp-synhighlighter-outer"><div id="wpshdt_6" class="wp-synhighlighter-expanded"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_6"></a><a id="wpshat_6" class="wp-synhighlighter-title" href="#codesyntax_6"  onClick="javascript:wpsh_toggleBlock(6)" title="Click to show/hide code block">Source code</a></td><td align="right"><a href="#codesyntax_6" onClick="javascript:wpsh_code(6)" title="Show code only"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_6" onClick="javascript:wpsh_print(6)" title="Print code"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_6" class="wp-synhighlighter-inner" style="display: block;"><pre class="powershell" style="font-family:monospace;">Import<span class="sy0">-</span>Module Az
Connect<span class="sy0">-</span>AzAccount <span class="sy0">-</span>Subscription <span class="st0">'&lt;your azure subscriptionid&gt;'</span> <span class="sy0">-</span>Tenant <span class="st0">'&lt;the Tenant ID&gt;'</span>
&nbsp;
<span class="re0">$resourceGroupName</span> <span class="sy0">=</span> <span class="st0">&quot;&lt;your existing resource group name&gt;&quot;</span>
<span class="re0">$adminLogin</span> <span class="sy0">=</span> <span class="st0">&quot;&lt;your Azure SQL Server login&gt;&quot;</span>
<span class="re0">$adminPassword</span> <span class="sy0">=</span> <span class="st0">&quot;&lt; your Azure SQL Server password&gt;&quot;</span>
<span class="re0">$adminPasswordSecure</span> <span class="sy0">=</span> <span class="kw1">ConvertTo-SecureString</span> <span class="kw5">-String</span> <span class="re0">$AdminPassword</span> <span class="kw5">-AsPlainText</span> <span class="kw5">-Force</span>
<span class="re0">$adminCred</span> <span class="sy0">=</span> <span class="kw1">New-Object</span> <span class="kw5">-TypeName</span> <span class="st0">&quot;System.Management.Automation.PSCredential&quot;</span> <span class="kw5">-ArgumentList</span> <span class="re0">$adminLogin</span><span class="sy0">,</span> <span class="re0">$adminPasswordSecure</span>
<span class="re0">$agentServer</span> <span class="sy0">=</span> <span class="st0">&quot;&lt;your Azure SQL Server&gt;&quot;</span>
<span class="re0">$ejagentName</span> <span class="sy0">=</span> <span class="st0">&quot;&lt;Your Elastic Job Agent name&gt;&quot;</span>
&nbsp;
<span class="co1"># create the job database</span>
<span class="kw1">Write-Output</span> <span class="st0">&quot;Creating a blank database to be used as the Job Database...&quot;</span>
<span class="re0">$jobDatabaseName</span> <span class="sy0">=</span> <span class="st0">&quot;JobDatabase&quot;</span>
<span class="re0">$jobDatabase</span> <span class="sy0">=</span> New<span class="sy0">-</span>AzSqlDatabase <span class="sy0">-</span>ResourceGroupName <span class="re0">$resourceGroupName</span> <span class="sy0">-</span>ServerName <span class="re0">$agentServer</span> <span class="sy0">-</span>DatabaseName <span class="re0">$jobDatabaseName</span> <span class="sy0">-</span>RequestedServiceObjectiveName <span class="st0">&quot;S1&quot;</span>
<span class="re0">$jobDatabase</span>
&nbsp;
<span class="kw1">Write-Output</span> <span class="st0">&quot;Getting databases...&quot;</span>
<span class="re0">$db1</span> <span class="sy0">=</span> Get<span class="sy0">-</span>AzSqlDatabase <span class="sy0">-</span>ResourceGroupName <span class="re0">$resourceGroupName</span> <span class="sy0">-</span>ServerName <span class="re0">$agentServer</span> <span class="sy0">-</span>DatabaseName <span class="st0">&quot;database1&quot;</span>
<span class="re0">$db1</span>
<span class="re0">$db2</span> <span class="sy0">=</span> Get<span class="sy0">-</span>AzSqlDatabase <span class="sy0">-</span>ResourceGroupName <span class="re0">$resourceGroupName</span> <span class="sy0">-</span>ServerName <span class="re0">$agentServer</span> <span class="sy0">-</span>DatabaseName <span class="st0">&quot;database2&quot;</span>
<span class="re0">$db2</span>
&nbsp;
&nbsp;
<span class="kw1">Write-Output</span> <span class="st0">&quot;Creating job agent...&quot;</span>
<span class="re0">$agentName</span> <span class="sy0">=</span> <span class="re0">$ejagentName</span>
<span class="re0">$jobAgent</span> <span class="sy0">=</span> <span class="re0">$jobDatabase</span> <span class="sy0">|</span> New<span class="sy0">-</span>AzSqlElasticJobAgent <span class="kw5">-Name</span> <span class="re0">$agentName</span>
<span class="re0">$jobAgent</span>
&nbsp;
<span class="co1"># in the master database (target server) create the master user login, master user, and job user login</span>
<span class="kw1">Write-Output</span> <span class="st0">&quot;Creating logins and users...&quot;</span>
<span class="re0">$params</span> <span class="sy0">=</span> <span class="sy0">@</span><span class="br0">&#123;</span>
  <span class="st0">'database'</span> <span class="sy0">=</span> <span class="st0">'master'</span>
  <span class="st0">'serverInstance'</span> <span class="sy0">=</span>  <span class="re0">$agentServer</span> <span class="sy0">+</span> <span class="st0">'.database.windows.net'</span>
  <span class="st0">'username'</span> <span class="sy0">=</span> <span class="re0">$adminLogin</span>
  <span class="st0">'password'</span> <span class="sy0">=</span> <span class="re0">$adminPassword</span>
  <span class="st0">'outputSqlErrors'</span> <span class="sy0">=</span> <span class="re0">$true</span>
  <span class="st0">'query'</span> <span class="sy0">=</span> <span class="st0">'CREATE LOGIN masteruser WITH PASSWORD='</span><span class="st0">'&lt;provide a password&gt;'</span><span class="st0">''</span>
<span class="br0">&#125;</span>
Invoke<span class="sy0">-</span>SqlCmd <span class="sy0">@</span>params
&nbsp;
<span class="re0">$params</span>.query <span class="sy0">=</span> <span class="st0">&quot;CREATE USER masteruser FROM LOGIN masteruser&quot;</span>
Invoke<span class="sy0">-</span>SqlCmd <span class="sy0">@</span>params
<span class="re0">$params</span>.query <span class="sy0">=</span> <span class="st0">'CREATE LOGIN jobuser WITH PASSWORD='</span><span class="st0">'&lt;provide a password&gt;'</span><span class="st0">''</span>
Invoke<span class="sy0">-</span>SqlCmd <span class="sy0">@</span>params
&nbsp;
<span class="co1"># for each target database</span>
<span class="co1"># create the jobuser from jobuser login and check permission for script execution</span>
<span class="re0">$targetDatabases</span> <span class="sy0">=</span> <span class="sy0">@</span><span class="br0">&#40;</span> <span class="re0">$db1</span>.DatabaseName<span class="sy0">,</span> <span class="re0">$Db2</span>.DatabaseName <span class="br0">&#41;</span>
<span class="re0">$createJobUserScript</span> <span class="sy0">=</span>  <span class="st0">&quot;CREATE USER jobuser FROM LOGIN jobuser&quot;</span>
<span class="re0">$addrolememberScript</span> <span class="sy0">=</span> <span class="st0">&quot;EXEC sp_addrolemember 'db_owner', 'jobuser';&quot;</span>
&nbsp;
<span class="re0">$targetDatabases</span> <span class="sy0">|</span> <span class="sy0">%</span> <span class="br0">&#123;</span>
  <span class="re0">$params</span>.database <span class="sy0">=</span> <a href="about:blank"><span class="kw6">$_</span></a>
  <span class="re0">$params</span>.query <span class="sy0">=</span> <span class="re0">$createJobUserScript</span>
  Invoke<span class="sy0">-</span>SqlCmd <span class="sy0">@</span>params
  <span class="re0">$params</span>.query <span class="sy0">=</span> <span class="re0">$addrolememberScript</span>
  Invoke<span class="sy0">-</span>SqlCmd <span class="sy0">@</span>params
&nbsp;
<span class="br0">&#125;</span>
&nbsp;
<span class="co1"># create job credential in Job database for master user</span>
<span class="kw1">Write-Output</span> <span class="st0">&quot;Creating job credentials...&quot;</span>
<span class="re0">$loginPasswordSecure</span> <span class="sy0">=</span> <span class="br0">&#40;</span><span class="kw1">ConvertTo-SecureString</span> <span class="kw5">-String</span> <span class="st0">'&lt;enter a password&gt;'</span> <span class="kw5">-AsPlainText</span> <span class="kw5">-Force</span><span class="br0">&#41;</span>
&nbsp;
<span class="re0">$masterCred</span> <span class="sy0">=</span> <span class="kw1">New-Object</span> <span class="kw5">-TypeName</span> <span class="st0">&quot;System.Management.Automation.PSCredential&quot;</span> <span class="kw5">-ArgumentList</span> <span class="st0">&quot;masteruser&quot;</span><span class="sy0">,</span> <span class="re0">$loginPasswordSecure</span>
<span class="re0">$masterCred</span> <span class="sy0">=</span> <span class="re0">$jobAgent</span> <span class="sy0">|</span> New<span class="sy0">-</span>AzSqlElasticJobCredential <span class="kw5">-Name</span> <span class="st0">&quot;masteruser&quot;</span> <span class="kw5">-Credential</span> <span class="re0">$masterCred</span>
&nbsp;
<span class="re0">$jobCred</span> <span class="sy0">=</span> <span class="kw1">New-Object</span> <span class="kw5">-TypeName</span> <span class="st0">&quot;System.Management.Automation.PSCredential&quot;</span> <span class="kw5">-ArgumentList</span> <span class="st0">&quot;jobuser&quot;</span><span class="sy0">,</span> <span class="re0">$loginPasswordSecure</span>
<span class="re0">$jobCred</span> <span class="sy0">=</span> <span class="re0">$jobAgent</span> <span class="sy0">|</span> New<span class="sy0">-</span>AzSqlElasticJobCredential <span class="kw5">-Name</span> <span class="st0">&quot;jobuser&quot;</span> <span class="kw5">-Credential</span> <span class="re0">$jobCred</span>
&nbsp;
&nbsp;
<span class="kw1">Write-Output</span> <span class="st0">&quot;Creating test target groups...&quot;</span>
<span class="co1"># create ServerGroup target group</span>
<span class="re0">$serverGroup</span> <span class="sy0">=</span> <span class="re0">$jobAgent</span> <span class="sy0">|</span> New<span class="sy0">-</span>AzSqlElasticJobTargetGroup <span class="kw5">-Name</span> <span class="st0">'ServerGroup'</span>
<span class="re0">$serverGroup</span> <span class="sy0">|</span> Add<span class="sy0">-</span>AzSqlElasticJobTarget <span class="sy0">-</span>ServerName <span class="re0">$agentServer</span> <span class="sy0">-</span>RefreshCredentialName <span class="re0">$masterCred</span>.CredentialName
<span class="re0">$serverGroup</span> <span class="sy0">|</span> Add<span class="sy0">-</span>AzSqlElasticJobTarget <span class="sy0">-</span>ServerName <span class="re0">$agentServer</span> <span class="sy0">-</span>DatabaseName <span class="re0">$jobDatabase</span>.DatabaseName <span class="kw5">-Exclude</span>
<span class="co1">#$serverGroup | Add-AzSqlElasticJobTarget -ServerName $agentServer -DatabaseName $db1.DatabaseName -RefreshCredentialName $masterCred.CredentialName</span>
<span class="co1">#$serverGroup | Add-AzSqlElasticJobTarget -ServerName $agentServer -DatabaseName $db2.DatabaseName -RefreshCredentialName $masterCred.CredentialName</span>
&nbsp;
&nbsp;
<span class="kw1">Write-Output</span> <span class="st0">&quot;Creating a new job...&quot;</span>
<span class="re0">$jobName</span> <span class="sy0">=</span> <span class="st0">&quot;IndexOptimize&quot;</span>
<span class="re0">$job</span> <span class="sy0">=</span> <span class="re0">$jobAgent</span> <span class="sy0">|</span> New<span class="sy0">-</span>AzSqlElasticJob <span class="kw5">-Name</span> <span class="re0">$jobName</span> <span class="sy0">-</span>RunOnce
<span class="re0">$job</span>
&nbsp;
<span class="kw1">Write-Output</span> <span class="st0">&quot;Creating job steps...&quot;</span>
<span class="re0">$sqlText1</span> <span class="sy0">=</span> <span class="st0">&quot;EXECUTE [dbo].[IndexOptimize] @Databases = 'USER_DATABASES', @UpdateStatistics = 'ALL', @LogToTable = 'Y'&quot;</span>
&nbsp;
<span class="re0">$job</span> <span class="sy0">|</span> Add<span class="sy0">-</span>AzSqlElasticJobStep <span class="kw5">-Name</span> <span class="st0">&quot;Index Optimize User Databases&quot;</span> <span class="sy0">-</span>TargetGroupName <span class="re0">$serverGroup</span>.TargetGroupName <span class="sy0">-</span>CredentialName <span class="re0">$jobCred</span>.CredentialName <span class="sy0">-</span>CommandText <span class="re0">$sqlText1</span>
&nbsp;
&nbsp;
<span class="kw1">Write-Output</span> <span class="st0">&quot;Scheduling the job...&quot;</span>
<span class="re0">$job</span> <span class="sy0">|</span> Set<span class="sy0">-</span>AzSqlElasticJob <span class="sy0">-</span>IntervalType Day <span class="sy0">-</span>IntervalCount <span class="nu0">1</span> <span class="sy0">-</span>StartTime <span class="br0">&#40;</span><span class="st0">'11/23/2022 20:00:00'</span><span class="br0">&#41;</span> <span class="sy0">-</span>Enable
<span class="co1">#$job | Set-AzSqlElasticJob -IntervalType Hour -IntervalCount 1 -StartTime ('11/17/2022 10:00:00') -Enable   #set to  hour for testing</span>
&nbsp;
<span class="co1">#Write-Output &quot;Start a new execution of the job...&quot;</span>
<span class="co1">#$jobExecution = $job | Start-AzSqlElasticJob</span>
<span class="co1">#$jobExecution</span></pre></div></div>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph"><em><strong>And there you have it</strong></em>, you have successfully created an Azure Elastic Job to automate your index optimization.</p>



<p class="wp-block-paragraph">Selecting the Elastic Job agent in the portal, on the Overview page you will see the last 100 job executions:</p>



<figure class="wp-block-image"><img decoding="async" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/08/a-screenshot-of-a-computer-description-automatica-1.png" alt="A screenshot of a computer Description automatically generated"/></figure>



<p class="wp-block-paragraph">Selecting the Jobs page will list all the jobs associated with the Elastic Job agent.</p>



<figure class="wp-block-image"><img decoding="async" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/08/a-screenshot-of-a-computer-description-automatica-2.png" alt="A screenshot of a computer Description automatically generated"/></figure>



<h3 class="wp-block-heading">Pros and Cons</h3>



<p class="wp-block-paragraph">Having worked with this for a while, there were some things that I want to highlight in terms of what I liked and some areas that could be better.</p>



<p class="wp-block-paragraph">I’ll start with the Cons. I’m not a PowerShell guru so it took me a while to get the PowerShell exactly right. A part of that struggle could be, again, that this is in preview and so everything isn’t completely “baked”. Additionally, the UI isn’t the best but I know Microsoft is working on that and should have a better UI experience once released.</p>



<p class="wp-block-paragraph">In the “pros” category, I like the fact that there is no timeout issue that you have with Runbooks and Azure Functions. That is awesome. I also love that you can create a group, add and remove databases from it, and done. Run your stuff against one or more databases. BAM! Lastly, you can do all of this in either PowerShell or T-SQL. Slick.</p>



<p class="wp-block-paragraph">Reading the last couple blogs posts was probably an eye sore, as there were a ton of steps to get Azure automation created, etc. For Elastic Jobs, a couple of clicks, done. Again, the hard part was getting the PowerShell correct.</p>



<h3 class="wp-block-heading">Summary</h3>



<p class="wp-block-paragraph">This blog post just scratches the surface of what Elastic Jobs can do, but we often see companies not doing index maintenance in Azure because either they don’t know how to do it or it is too difficult. Hopefully this will help get people going.</p>



<p class="wp-block-paragraph">Hey, if you need help with any of this, ping me! I’m happy to help!</p>
<p>The post <a href="https://sqlsolutionsgroup.com/index-maintenance-automating-azure-with-elastic-jobs/">Index Maintenance: Automating Azure with Elastic Jobs</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlsolutionsgroup.com/index-maintenance-automating-azure-with-elastic-jobs/feed/</wfw:commentRss>
			<slash:comments>11</slash:comments>
		
		
			</item>
		<item>
		<title>How To: Running SQL Jobs with Azure Functions</title>
		<link>https://sqlsolutionsgroup.com/how-to-running-sql-jobs-with-azure-functions/</link>
					<comments>https://sqlsolutionsgroup.com/how-to-running-sql-jobs-with-azure-functions/#comments</comments>
		
		<dc:creator><![CDATA[Scott Klein]]></dc:creator>
		<pubDate>Wed, 15 Mar 2023 15:21:43 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[#azure]]></category>
		<category><![CDATA[#microsftcertifedmaster]]></category>
		<category><![CDATA[#microsftpartner]]></category>
		<category><![CDATA[#SQLAB]]></category>
		<category><![CDATA[#SQlatino]]></category>
		<category><![CDATA[#SQlatinoamerica]]></category>
		<category><![CDATA[#sqldatabase]]></category>
		<category><![CDATA[#sqldeveloper]]></category>
		<category><![CDATA[#SQLgroupie]]></category>
		<category><![CDATA[#sqlimer]]></category>
		<category><![CDATA[#sqlimerbymay]]></category>
		<category><![CDATA[#sqlinjection]]></category>
		<category><![CDATA[#sqlinternals]]></category>
		<category><![CDATA[#sqlite]]></category>
		<category><![CDATA[#sqlite3]]></category>
		<category><![CDATA[#SQLLearning]]></category>
		<category><![CDATA[#SQLMagazine]]></category>
		<category><![CDATA[#sqlmanagementstudio]]></category>
		<category><![CDATA[#sqlmanager]]></category>
		<category><![CDATA[#Sqlmap]]></category>
		<category><![CDATA[#sqlrun]]></category>
		<category><![CDATA[#sqlsaturday2017]]></category>
		<category><![CDATA[#sqlsatvienna]]></category>
		<category><![CDATA[#sqlserver]]></category>
		<category><![CDATA[#SQLserver2012]]></category>
		<category><![CDATA[#sqlserver2014]]></category>
		<category><![CDATA[#sqlserver2017]]></category>
		<category><![CDATA[#sqlserver2022]]></category>
		<category><![CDATA[#SQLServeronLinux]]></category>
		<category><![CDATA[#SQLsolutionsgroup]]></category>
		<category><![CDATA[#SQLTraining]]></category>
		<category><![CDATA[#SQLYog]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQLPASS]]></category>
		<category><![CDATA[SQLSaturday]]></category>
		<category><![CDATA[SSG]]></category>
		<guid isPermaLink="false">https://sqlsolutionsgroup.com/?p=6861</guid>

					<description><![CDATA[<p>In my previous blog post I introduced Azure Automation and Runbooks and showed how to automate index maintenance using these Azure technologies.&#160;This blog post is the second in the series and discusses and illustrates how to automate index maintenance, but this time using Azure Functions. What are Azure Functions? Just as I did in my [&#8230;]</p>
<p>The post <a href="https://sqlsolutionsgroup.com/how-to-running-sql-jobs-with-azure-functions/">How To: Running SQL Jobs with Azure Functions</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In my previous <a href="https://sqlsolutionsgroup.com/azure-automation-and-runbooks/">blog post</a> I introduced Azure Automation and Runbooks and showed how to automate index maintenance using these Azure technologies.&nbsp;This blog post is the second in the series and discusses and illustrates how to automate index maintenance, but this time using Azure Functions.</p>
<h3>What are Azure Functions?</h3>
<p>Just as I did in my last blog post, let me begin by stating that this isn’t a deep dive into Azure Functions. We could do a whole series of posts just on Azure Functions itself, but for the purposes of this blog, I’ll do a brief introduction and summary simply to lay the appropriate foundation for this blog.</p>
<p>Azure Functions is an Azure serverless solution that provides compute on demand. The goal with Azure Functions is to provide a platform the enables you to write less code and deploy that code without maintaining a lot of infrastructure, all while supporting the demand needs. In other words, you write and deploy the code, Azure takes care of everything else. As incoming requests increase, Azure Functions automatically scales to meet the demand, and then as requests decrease, so do the resources.</p>
<p>Azure Functions offers different hosting plans, so you will want to become familiar with those if you are planning on using this service. There are three hosting plans:</p>
<ul>
<li>Consumption – Instances of Azure Functions are dynamically added and removed based on the number of incoming requests/events.</li>
<li>Premium – A dynamic scale hosting option that provides additional benefits for your functions.</li>
<li>Dedicated (App Service) – Defines a set of dedicated resources for an app to run.</li>
</ul>
<p>You can read more about the different hosting options <a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-scale">here</a>.</p>
<p>There are wide number of different scenarios in which to use Azure Functions, including processing data in real time, analyzing IoT data, building serverless workflows, and the topic which will be covering today, r<strong>unning scheduled tasks</strong>.</p>
<p>Obviously, there is much more to azure Function, but this should be enough to get us started, so with this foundation, let’s build an Azure Function to optimize indexes.</p>
<h3>Getting Started</h3>
<p>To get started, there are several things we need:</p>
<ol>
<li>An Azure SQL database</li>
<li>Scripts to do the index optimize</li>
<li>Visual Studio</li>
</ol>
<p>For Visual Studio, you will need the Azure Development components to get the Azure Function tools. Run the Visual Studio installer if needed and select the Azure Development component.</p>
<p><img decoding="async" class="alignnone wp-image-6830 size-full" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure1.png" alt="azure functions" width="624" height="201" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure1.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure1-300x97.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<h3>Create the Database</h3>
<p>I discussed this process in the previous blog, but in case you didn’t read that one, I’ll walk through this again. For this demo, I simply created a new General Purpose database, based off of the Sample database. For the purposes of this blog, no other configuration is needed.</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-6832 size-full" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure2.png" alt="Azure Functions" width="490" height="172" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure2.png 490w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure2-300x105.png 300w" sizes="(max-width: 490px) 100vw, 490px" /></p>
<h3>Run the Scripts</h3>
<p>With the database selected, the next step is to add the appropriate components to the database to do the index optimization. We at SSG are a huge fan of Ola Hallengren and his Maintenance Solution, and you can get his awesome scripts <a href="https://ola.hallengren.com/downloads.html">here</a>.</p>
<p>The IndexOptimize.sql script will run in Azure, but you will also need to download the CommandLog.sql file as well. Run both of those in the database you created.</p>
<h3>Create the Azure Function App</h3>
<p>With everything prepared, the next step is to create the Azure Function App. In the Azure Portal, type “Function App” in the search bar and select Function App.</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-6833 size-full" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure3.png" alt="Azure Functions" width="433" height="99" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure3.png 433w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure3-300x69.png 300w" sizes="(max-width: 433px) 100vw, 433px" /></p>
<p>Click <strong>+ Create</strong>.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6834" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure4.png" alt="azure functions" width="228" height="96"></p>
<p>There are a few things you will need select and fill out on most of the tabs, so we’ll walk through those.</p>
<h3><strong>Basics</strong></h3>
<p>In the <strong>Basics</strong> tab of the <strong>Create Function App</strong>, select the Subscription and Resource group, then provide an account name and region.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6815" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks10.png" alt="" width="182" height="259"> <img loading="lazy" decoding="async" class="alignnone size-medium wp-image-6836" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure5-300x154.png" alt="azure functions" width="300" height="154" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure5-300x154.png 300w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure5.png 624w" sizes="(max-width: 300px) 100vw, 300px" /></p>
<p>Additionally, you will need to indicate what you are publishing (in this case, some code), the runtime stack (I’m using .NET), the .NET version, and the region in which you want to create this Function App.</p>
<p>Lastly, select the operating system (due to my selections, only Windows is supported), and then select the plan type.</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-6837 size-full" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure6.png" alt="azure functions" width="624" height="339" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure6.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure6-300x163.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<h3>Hosting</h3>
<p>Select the Hosting tab, and either select an existing storage account or create a new one.</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-6838 size-full" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure7.png" alt="azure functions" width="624" height="153" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure7.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure7-300x74.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<h3>Networking</h3>
<p>On the Networking tab, select Off for <em>Enable public access</em>. Since this is running within Azure and you aren’t accessing from outside of Azure, there is no need to enable public access.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6839" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure8.png" alt="azure functions" width="624" height="136" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure8.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure8-300x65.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<h3>Monitoring</h3>
<p>On the monitoring tab, select to either enable or disable Application Insights. Application Insights provides performance monitoring for your application by collecting metrics and telemetry data and storing those in a blob storage account. You can then view this information as a way to see how your application is doing. The more you work with Azure Functions (and Azure in general), it might be worth enabling this. As you are testing, you could probably leave it off.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6840" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure9.png" alt="azure functions" width="624" height="276" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure9.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure9-300x133.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<h3>Deployment</h3>
<p>Unless you are doing any type of source control for your code with GitHub, leave the continuous deployment disabled. As you start doing more with Azure Functions, you might consider turning this on and doing more with source control and continuous deployment.</p>
<p><img loading="lazy" decoding="async" class="alignnone wp-image-6841 size-full" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure10.png" alt="azure functions" width="624" height="163" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure10.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure10-300x78.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>Don’t worry about anything on the other tabs. Click Review + Create to create the Function App. At this point you have the basis to start automating, and we’ll do that with Visual Studio.</p>
<h3>Create the Azure Function</h3>
<p>The next step is to create the function to run the index maintenance. Fire up Visual Studio and create a new project. Search for Azure Functions and select the Azure Functions project type, then click Next.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6842" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure11.png" alt="azure functions" width="624" height="167" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure11.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure11-300x80.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>Give your project a name, and then click Next.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6843" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure12.png" alt="azure functions" width="624" height="375" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure12.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure12-300x180.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>This next step is an important one, as you are asked to select what type of Azure Function application you want to create. As you can see in the following image, there are a lot of different types. Since we want to run our index maintenance on a schedule, select the Timer Trigger.</p>
<p>You are probably asking how the timer or schedule is defined. In the following screenshot you can see a schedule to the right with some funky format. Don’t worry, I’ll get to that shortly, so for now just click Create.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6844" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure13.png" alt="azure functions" width="624" height="435" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure13.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure13-300x209.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>The project will be created and a default function will be added, as shown in the following screenshot.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6845" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure14.png" alt="azure functions" width="624" height="251" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure14.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure14-300x121.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>For clarity, delete Function1 from the project, then right click the project and select Add -&gt; New Azure Function. In the Add New Item dialog select Azure Function and name it AzureMaintFunction. Click Add.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6846" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure15.png" alt="azure functions" width="624" height="433" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure15.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure15-300x208.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>For the type, select Timer trigger, leave the schedule format as is (I’ll get to that shortly) and click Add.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6847" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure16.png" alt="azure functions" width="624" height="407" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure16.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure16-300x196.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>Now we have a project and function with a meaningful name. 😊</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6848" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure17.png" alt="azure functions" width="624" height="251" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure17.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure17-300x121.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>OK, since the function is accessing a SQL database, the next step is to add the appropriate dependencies. Click on the Project menu, then select Manage NuGet packages. In the NuGet Package Manager, select nuget.org as the Package source, then search for system.data and select the System.Data.SqlClient package and click install.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6849" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure18.png" alt="azure functions" width="624" height="147" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure18.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure18-300x71.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>Once it is installed, add <em>System.Data</em> and <em>System.Data.SqlClient</em> references to the type of the Function.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6850" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure19.png" alt="azure functions" width="466" height="144" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure19.png 466w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure19-300x93.png 300w" sizes="(max-width: 466px) 100vw, 466px" /></p>
<p>Finally, it is time to add some code to your function. Copy and paste the following code into the Run method of the function, below the log.Info line.</p>
<p>There are a few values you will need to replace in the code. Replace &lt;server&gt; with the actual name of your Azure SQL Database server, replace &lt;user&gt; with the username, then replace &lt;pwd&gt; with the password.</p>
<p><div id="wpshdo_7" class="wp-synhighlighter-outer"><div id="wpshdt_7" class="wp-synhighlighter-expanded"><table border="0" width="100%"><tr><td align="left" width="80%"><a name="#codesyntax_7"></a><a id="wpshat_7" class="wp-synhighlighter-title" href="#codesyntax_7"  onClick="javascript:wpsh_toggleBlock(7)" title="Click to show/hide code block">Source code</a></td><td align="right"><a href="#codesyntax_7" onClick="javascript:wpsh_code(7)" title="Show code only"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/code.png" /></a>&nbsp;<a href="#codesyntax_7" onClick="javascript:wpsh_print(7)" title="Print code"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/printer.png" /></a>&nbsp;<a href="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/About.html" target="_blank" title="Show plugin information"><img decoding="async" border="0" style="border: 0 none" src="https://sqlsolutionsgroup.com/wp-content/plugins/wp-synhighlight/themes/default/images/info.gif" /></a>&nbsp;</td></tr></table></div><div id="wpshdi_7" class="wp-synhighlighter-inner" style="display: block;"><pre class="tsql" style="font-family:monospace;"><span class="kw1">USING</span> <span class="br0">&#40;</span><span class="kw2">VAR</span> conn <span class="sy0">=</span> <span class="kw1">NEW</span> SqlConnection<span class="br0">&#40;</span><span class="st0">&quot;Server=tcp:&lt;server&gt;.database.windows.net,1433;Initial Catalog=database4;Persist Security Info=False;User ID=&lt;user&gt;;Password=&lt;pwd&gt;;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=3600;&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
&nbsp;
<span class="br0">&#123;</span>
&nbsp;
try
&nbsp;
<span class="br0">&#123;</span>
&nbsp;
SqlCommand cmd <span class="sy0">=</span> <span class="kw1">NEW</span> SqlCommand<span class="br0">&#40;</span><span class="br0">&#41;</span>;
&nbsp;
cmd.<span class="me1">CommandText</span> <span class="sy0">=</span> <span class="st0">&quot;IndexOptimize&quot;</span>;
&nbsp;
cmd.<span class="kw1">PARAMETERS</span>.<span class="kw1">ADD</span><span class="br0">&#40;</span><span class="st0">&quot;@Databases&quot;</span>, SqlDbType.<span class="kw1">NVARCHAR</span><span class="br0">&#41;</span>;
&nbsp;
cmd.<span class="kw1">PARAMETERS</span><span class="br0">[</span><span class="st0">&quot;@Databases&quot;</span><span class="br0">]</span>.<span class="kw1">VALUE</span> <span class="sy0">=</span> <span class="st0">&quot;USER_DATABASES&quot;</span>;
&nbsp;
cmd.<span class="kw1">PARAMETERS</span>.<span class="kw1">ADD</span><span class="br0">&#40;</span><span class="st0">&quot;@UpdateStatistics&quot;</span>, SqlDbType.<span class="kw1">NVARCHAR</span><span class="br0">&#41;</span>;
&nbsp;
cmd.<span class="kw1">PARAMETERS</span><span class="br0">[</span><span class="st0">&quot;@UpdateStatistics&quot;</span><span class="br0">]</span>.<span class="kw1">VALUE</span> <span class="sy0">=</span> <span class="st0">&quot;ALL&quot;</span>;
&nbsp;
cmd.<span class="kw1">PARAMETERS</span>.<span class="kw1">ADD</span><span class="br0">&#40;</span><span class="st0">&quot;@LogToTable&quot;</span>, SqlDbType.<span class="kw1">NVARCHAR</span><span class="br0">&#41;</span>;
&nbsp;
cmd.<span class="kw1">PARAMETERS</span><span class="br0">[</span><span class="st0">&quot;@LogToTable&quot;</span><span class="br0">]</span>.<span class="kw1">VALUE</span> <span class="sy0">=</span> <span class="st0">&quot;Y&quot;</span>;
&nbsp;
cmd.<span class="me1">CommandType</span> <span class="sy0">=</span> CommandType.<span class="me1">StoredProcedure</span>;
&nbsp;
cmd.<span class="kw1">CONNECTION</span> <span class="sy0">=</span> conn;
&nbsp;
conn.<span class="kw1">OPEN</span><span class="br0">&#40;</span><span class="br0">&#41;</span>;
&nbsp;
cmd.<span class="me1">ExecuteNonQuery</span><span class="br0">&#40;</span><span class="br0">&#41;</span>;
&nbsp;
<span class="br0">&#125;</span>
&nbsp;
catch <span class="br0">&#40;</span><span class="kw1">EXCEPTION</span> ex<span class="br0">&#41;</span>
&nbsp;
<span class="br0">&#123;</span>
&nbsp;
<span class="kw2">LOG</span>.<span class="me1">Info</span><span class="br0">&#40;</span>ex.<span class="me1">Message</span><span class="br0">&#41;</span>;
&nbsp;
<span class="br0">&#125;</span>
&nbsp;
<span class="br0">&#125;</span></pre></div></div></p>
<p>Before moving on, I’ll explain what this code does. First, this defines a connection to the Azure SQL Database. A critical piece of the connection string is the Connection Timeout. This is critical to remember, as it is standard .NET stuff. As I explained in the previous blog post, if the IndexOptimize stored procedure does NOT complete before the timeout period is reached, it will time out and roll back the item it is currently working on.</p>
<p>Unlike PowerShell, the SQLConnection has a default timeout, so even if you omit the Connection Timeout parameter, it will still timeout after the default time. So for Azure Functions, you will need to know roughly how long the index optimize will take and set the timeout appropriately.</p>
<p>The rest of the code defines the parameters being passed to the IndexOptimize stored procedure, then opens the connection and executes the query.</p>
<p>OK, let’s discuss the timer trigger. Azure Functions timer trigger uses the NCronTab library which is super similar to the CRON expressions. The difference is that the NCronTab library uses an additional sixth field at the beginning for a time precision in seconds.</p>
<p>Thus, the format looks like this:</p>
<p>{second} {minute} {hour} {day} {month} {year}</p>
<p>You can read more about Azure Function timer triggers <a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-timer?tabs=in-process&amp;pivots=programming-language-csharp">here</a>, and NCrontab for .NET <a href="https://github.com/atifaziz/NCrontab">here</a>.</p>
<p>So, the default timer essentially has the function firing 12 times an hour, or at second 0 of every 5<sup>th</sup> minute of every hour each day. Not what we want.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6851" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure20.png" alt="azure functions" width="624" height="67" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure20.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure20-300x32.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>For simplicity, I just want my function to fire every day at 10pm, Monday through Saturday (day 0 is Sunday). So, I change the timer as follows:</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6852" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure21.png" alt="azure functions" width="624" height="90" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure21.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure21-300x43.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>Thus, it will automatically fire Monday through Saturday at 10pm.</p>
<h3>Deploy the Function</h3>
<p>The next step is to Deploy the function. First, build the project to ensure it compiles, then right click the Project and select Publish.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6853" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure22.png" alt="azure functions" width="297" height="196"></p>
<p>In the Publish dialog, select Azure.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6854" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure23.png" alt="azure functions" width="624" height="439" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure23.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure23-300x211.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>If prompted to sign in, do so.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6855" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure24.png" alt="azure functions" width="624" height="224" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure24.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure24-300x108.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>Lastly, select the subscription if not already populated, then in the Function Apps area, select the Function App you created earlier, then click Finish.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6856" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure25.png" alt="azure functions" width="624" height="436" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure25.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure25-300x210.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>The Function will be published and after a few minutes you will be informed that the publish has, hopefully, been successful.</p>
<p>You can tell the history of the events, or runs, of the functions by looking in the Azure portal at your Azure Function and looking at the Monitor.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-6857" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure26.png" alt="azure functions" width="624" height="483" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure26.png 624w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/03/Azure26-300x232.png 300w" sizes="(max-width: 624px) 100vw, 624px" /></p>
<p>And there you have it, you have successfully created an Azure Function to automate your SQL index maintenance.</p>
<h3>Pros and Cons</h3>
<p>Having worked with this for a while, there are some things that I want to highlight in terms of what I liked and some areas that could be better.</p>
<p>In the “pros” category, I like that you can do this right in Visual Studio (or even VSCode), and the Azure Functions template make it super easy to use and work with Azure Functions. Sweet. It was also pretty easy to publish the function. Very sweet.</p>
<p>I haven’t tested it yet but since it is standard .NET I’m hoping you can “obfuscate” critical variables, such as username and passwords by putting them in an app.config.</p>
<p>For the downsides, just like Azure Automation and Runbooks, it is “per database”, meaning, even though the query says “User Databases” (if you are familiar with Ola’s scripts), it is ONLY going to run the index optimize against the database specified in the parameter. That is just a limitation of Azure, not the Runbook. Thus, if you want to optimize indexes against multiple databases, you need multiple Functions or modify the one function to connect to multiple databases. This can get kinda messy.</p>
<p>CRON Expressions. Ugh. Once you work with them a bit it gets easier, but I had to do a lot of reading just to get started with them.</p>
<h3>Summary</h3>
<p>This blog post just touches the surface of what Azure Functions can do, but we often see companies not doing index maintenance in Azure because either they don’t know how to do it or it is too difficult. Hopefully this will help get people going.</p>
<p>In the next two blog posts I’ll show how to do the same thing using Azure Elastic Jobs.</p>
<p>The post <a href="https://sqlsolutionsgroup.com/how-to-running-sql-jobs-with-azure-functions/">How To: Running SQL Jobs with Azure Functions</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlsolutionsgroup.com/how-to-running-sql-jobs-with-azure-functions/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>How To: Azure Automation and Runbooks</title>
		<link>https://sqlsolutionsgroup.com/azure-automation-and-runbooks/</link>
					<comments>https://sqlsolutionsgroup.com/azure-automation-and-runbooks/#comments</comments>
		
		<dc:creator><![CDATA[Scott Klein]]></dc:creator>
		<pubDate>Wed, 15 Feb 2023 05:58:38 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[#microsftcertifedmaster]]></category>
		<category><![CDATA[#microsftpartner]]></category>
		<category><![CDATA[#SQLAB]]></category>
		<category><![CDATA[#SQlatino]]></category>
		<category><![CDATA[#SQlatinoamerica]]></category>
		<category><![CDATA[#sqldatabase]]></category>
		<category><![CDATA[#sqldeveloper]]></category>
		<category><![CDATA[#SQLgroupie]]></category>
		<category><![CDATA[#sqlimer]]></category>
		<category><![CDATA[#sqlimerbymay]]></category>
		<category><![CDATA[#sqlinjection]]></category>
		<category><![CDATA[#sqlinternals]]></category>
		<category><![CDATA[#sqlite]]></category>
		<category><![CDATA[#sqlite3]]></category>
		<category><![CDATA[#SQLLearning]]></category>
		<category><![CDATA[#SQLMagazine]]></category>
		<category><![CDATA[#sqlmanagementstudio]]></category>
		<category><![CDATA[#sqlmanager]]></category>
		<category><![CDATA[#Sqlmap]]></category>
		<category><![CDATA[#sqlrun]]></category>
		<category><![CDATA[#sqlsaturday2017]]></category>
		<category><![CDATA[#sqlsatvienna]]></category>
		<category><![CDATA[#sqlserver]]></category>
		<category><![CDATA[#SQLserver2012]]></category>
		<category><![CDATA[#sqlserver2014]]></category>
		<category><![CDATA[#sqlserver2017]]></category>
		<category><![CDATA[#sqlserver2022]]></category>
		<category><![CDATA[#SQLServeronLinux]]></category>
		<category><![CDATA[#SQLsolutionsgroup]]></category>
		<category><![CDATA[#SQLTraining]]></category>
		<category><![CDATA[#SQLYog]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQLPASS]]></category>
		<category><![CDATA[SQLSaturday]]></category>
		<category><![CDATA[SSG]]></category>
		<guid isPermaLink="false">https://sqlsolutionsgroup.com/?p=6755</guid>

					<description><![CDATA[<p>In my previous &#8220;prologue&#8221; blog post I introduced a small handful of topics that discuss how to automate things in Azure, specifically SQL agent jobs. This stems from recent needs we&#8217;ve had at SSG to automate several of our Azure clients’ processes, especially tasks like rebuilding indexes and updating statistics. This first (real) post in [&#8230;]</p>
<p>The post <a href="https://sqlsolutionsgroup.com/azure-automation-and-runbooks/">How To: Azure Automation and Runbooks</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><a href="https://sqlsolutionsgroup.com/run-sql-jobs-in-azure-a-prologue/">In my previous &#8220;prologue&#8221; blog post</a> I introduced a small handful of topics that discuss how to automate things in Azure, specifically SQL agent jobs. This stems from recent needs we&#8217;ve had at SSG to automate several of our Azure clients’ processes, especially tasks like rebuilding indexes and updating statistics. This first (real) post in the series discusses how to run agent jobs. More specifically, this post focuses on index maintenance using Azure Automation and Runbooks. </p>



<p class="wp-block-paragraph">Follow-up blog posts will discuss doing the same thing using Azure Functions and Azure Elastic Jobs.</p>



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



<h3 class="wp-block-heading">What is Azure Automation?</h3>



<p class="wp-block-paragraph">Let me begin by stating that this isn’t a deep dive into Azure Automation, simply because we could do a whole blog series just on that. Let us know if you’d like to see that!. This will provide a quick intro and summary of what Azure Automation is to lay the appropriate foundation for this blog.</p>



<p class="wp-block-paragraph">In its simplest terms, <strong>Azure Automation is a cloud-based automation service that allows you to orchestrate and automate processes across your Azure and non-Azure environments</strong>. Azure Automation allows you to use several methods for accomplishing this, including Powershell and Python to provide the process automation capabilities.</p>



<p class="wp-block-paragraph">Process Automation is a key tenet of Azure Automation, and provides the ability to automate many of the repetitive tasks or processes in Azure. The key to enabling Process Automation is contained within an operating environment within Azure Automation called Runbooks.</p>



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



<h3 class="wp-block-heading">Runbooks</h3>



<p class="wp-block-paragraph">A Runbook is the component within Azure Automation which enables the automation of repetitive and time-consuming tasks in Azure. In their initial incarnation, Runbooks were simply Powershell workflows that contained the automation logic you wished to implement.</p>



<p class="wp-block-paragraph">Since then, Runbooks have been expanded to include and support both Python and graphical type Runbooks. For the purposes of this blog, we will create and use a Powershell Runbook. Azure Automation executes the Runbook based on the logic defined within the Runbook, which constitutes a single execution <em>instance</em> of the Runbook.</p>



<p class="wp-block-paragraph">So, with this foundation, let’s build a Runbook to optimize indexes.</p>



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



<h3 class="wp-block-heading">Getting Started</h3>



<p class="wp-block-paragraph">To get started, we need:</p>



<ol class="wp-block-list">
<li>An Azure SQL database</li>



<li>Scripts to do the index optimize</li>



<li>An Azure Automation Account (and Runbook)</li>
</ol>



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



<h3 class="wp-block-heading">Create the Database</h3>



<p class="wp-block-paragraph">For this blog, I simply created a new General Purpose database, based off of the Sample database. For the purposes of this blog, no other configuration is needed.&nbsp;</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="614" height="217" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks1a.png" alt="Azure Automation and Runbooks" class="wp-image-6806" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks1a.png 614w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks1a-300x106.png 300w" sizes="(max-width: 614px) 100vw, 614px" /></figure>



<h3 class="wp-block-heading">Run the Scripts</h3>



<p class="wp-block-paragraph">With the database selected, the next step is to add the appropriate components to the database for index optimization. We at SSG are huge fans of Ola Hallengren and his Maintenance Solution.  <strong>Get his awesome scripts <a href="https://ola.hallengren.com/downloads.html">here</a></strong>.</p>



<p class="wp-block-paragraph">The IndexOptimize.sql script will run in Azure, but you will also need to download the CommandLog.sql file as well (run this one first). Run both of those in the database you created.</p>



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



<h3 class="wp-block-heading">Create the Azure Automation Account</h3>



<p class="wp-block-paragraph">With everything prepared, the next step is to create the Azure Automation account. In the Azure Portal, type “automation” in the search bar and select Automation Accounts.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="438" height="207" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks2.png" alt="Azure Automation and Runbooks" class="wp-image-6807" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks2.png 438w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks2-300x142.png 300w" sizes="(max-width: 438px) 100vw, 438px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">Click <strong>+ Create</strong>.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="306" height="108" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks3.png" alt="Choose create" class="wp-image-6808" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks3.png 306w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks3-300x106.png 300w" sizes="(max-width: 306px) 100vw, 306px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">In the <strong>Basics</strong> tab of the <strong>Create an Automation Account</strong>, select the Subscription and Resource group. Then provide an account name and region.&nbsp;</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="720" height="430" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks4.png" alt="Create an Automation Account" class="wp-image-6809" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks4.png 720w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks4-300x179.png 300w" sizes="(max-width: 720px) 100vw, 720px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">Don’t worry about anything on the other tabs. Click Review + Create to create the Automation Account. At this point you have the basis to start automating. We’ll do that next by creating a Runbook.</p>



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



<h3 class="wp-block-heading">Create the Runbook</h3>



<p class="wp-block-paragraph">The next step is to create the Runbook. In the Navigation pane, scroll down to the Process Automation section and select Runbooks.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="229" height="175" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks5.png" alt="Create the runbook" class="wp-image-6810"/></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">You’ll see a list of default Runbooks, but create a new one by clicking on the + Create a runbook button.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="410" height="161" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks6.png" alt="Create a new runbook" class="wp-image-6811" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks6.png 410w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks6-300x118.png 300w" sizes="(max-width: 410px) 100vw, 410px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">In the Create a runbook page, provide a name for the Runbook (I used IndexOptimize for this example). Then select PowerShell for the Runbook type, and select the Runtime version of 7.1.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="710" height="257" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks7.png" alt="select PowerShell for the Runbook type, and select the Runtime version of 7.1." class="wp-image-6812" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks7.png 710w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks7-300x109.png 300w" sizes="(max-width: 710px) 100vw, 710px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">Click Create.</p>



<p class="wp-block-paragraph">You’ll see a blank canvas with which to enter your PowerShell code.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="846" height="309" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks8.png" alt="enter your PowerShell code here" class="wp-image-6813" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks8.png 846w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks8-300x110.png 300w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks8-768x281.png 768w" sizes="(max-width: 846px) 100vw, 846px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">In the right pane, enter the following PowerShell code (I’ll walk through it shortly):</p>



<pre title="PowerShell" class="wp-block-code"><code class="">$SQLServerName&nbsp;=&nbsp;Get-AutomationVariable&nbsp;-Name&nbsp;"sqlserver"

$database&nbsp;=&nbsp;Get-AutomationVariable&nbsp;-Name&nbsp;"database"

$username&nbsp;=&nbsp;Get-AutomationVariable&nbsp;-Name&nbsp;"username"

$password&nbsp;=&nbsp;Get-AutomationVariable&nbsp;-Name&nbsp;"password"

Invoke-Sqlcmd&nbsp;-ServerInstance&nbsp;$SQLServerName&nbsp;-Database&nbsp;$database&nbsp;-Username&nbsp;$username&nbsp;-Password&nbsp;$password&nbsp;-Query&nbsp;"EXECUTE&nbsp;[dbo].[IndexOptimize]&nbsp;@Databases&nbsp;=&nbsp;'USER_DATABASES',&nbsp;@UpdateStatistics&nbsp;=&nbsp;'ALL',&nbsp;@LogToTable&nbsp;=&nbsp;'Y'"&nbsp;-QueryTimeout&nbsp;65535&nbsp;-ConnectionTimeout&nbsp;60&nbsp;-Verbose</code></pre>



<p class="wp-block-paragraph">Click Save.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="656" height="164" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks9.png" alt="Save your PowerShell" class="wp-image-6814" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks9.png 656w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks9-300x75.png 300w" sizes="(max-width: 656px) 100vw, 656px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">We haven&#8217;t yet published at this point, which you will do once you have tested it. But before you do that, you need to configure some variables. So, go back to your Automation Account and in the navigation pane scroll down to the Shared Resources section. Select the Variables page.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="182" height="259" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks10.png" alt="Select the Variables page" class="wp-image-6815"/></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">Click on the Add a variable button and add the four variables used in the code above (and as listed in the image below). Specify the appropriate values for each. In this example, for simplicity’s sake, we are just using SQL Authentication.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="312" height="247" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks11.png" alt="Set your variables" class="wp-image-6798" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks11.png 312w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks11-300x238.png 300w" sizes="(max-width: 312px) 100vw, 312px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">Now it&#8217;s time to test. Go back and select your Index Optimize Runbook, then click the Edit button. Before we test it, I’ll explain what the code is doing. The first four lines grab the values from the variables in order to connect to the Azure server and database.</p>



<p class="wp-block-paragraph">The next line simply calls the PowerShell Invoke-Sqlcmd cmdlet, passing it the parameters from the first four lines, as well as a query to execute. In this case, it&#8217;s Ola’s IndexOptimize stored procedure installed earlier. In this example we are simply passing some default values to the stored procedure. If you want to know what values you should be passing in your environment, feel free to reach out to us at <a href="mailto:in**@***************up.com" data-original-string="kBO0jUeF0yq/A+mHgGTvnQ==2edUIamxhmsFEfUxPp0E7QWsAlXjUyLARmZ2/TH2XOPm0Q=" title="This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser."><span 
                data-original-string='L7cBjlD/5bKRA9XLIxkrlw==2edxBClmwwibffTT77YF88DJCGCP0R/304nsJJ6aJJE1WM='
                class='apbct-email-encoder'
                title='This contact has been encoded by Anti-Spam by CleanTalk. Click to decode. To finish the decoding make sure that JavaScript is enabled in your browser.'>in<span class="apbct-blur">**</span>@<span class="apbct-blur">***************</span>up.com</span></a> 😊).</p>



<p class="wp-block-paragraph">Now, the key to all of this is the <em>-QueryTimeout</em> parameter. The Runbook is essentially executing a query just like you normally would in Powershell or any other application that connects to a database.</p>



<p class="wp-block-paragraph"><em><strong>Note</strong>: In this example, I have specified a timeout value.</em> I<em>f you look at the Powershell documentation for the Invoke-Sqlcmd cmdlet, omitting the parameter means that the query will not timeout. </em></p>



<p class="wp-block-paragraph"><em>That last part is important because if you specify a timeout period and the IndexOptimize stored procedure does NOT complete before the timeout period is reached, it will time out and roll back the item it is currently working on. So, either omit the timeout parameter or know how long the job will run.</em></p>



<p class="wp-block-paragraph">It is now time to test it. Click on the Test pane button.&nbsp;</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="409" height="117" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks12.png" alt="Click on Test Pane" class="wp-image-6799" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks12.png 409w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks12-300x86.png 300w" sizes="(max-width: 409px) 100vw, 409px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">Click the Start button to run the code.&nbsp;</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="872" height="333" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks13.png" alt="Click the Start button to run the code" class="wp-image-6800" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks13.png 872w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks13-300x115.png 300w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks13-768x293.png 768w" sizes="(max-width: 872px) 100vw, 872px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">The job will be queued and then run. Because I included the -Verbose parameter, when the job finished my test window looks like this:</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="958" height="659" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/text-description-automatically-generated.png" alt="The finished job window" class="wp-image-6769" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/text-description-automatically-generated.png 958w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/text-description-automatically-generated-300x206.png 300w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/text-description-automatically-generated-768x528.png 768w" sizes="(max-width: 958px) 100vw, 958px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">You can also validate that it ran by connecting to the database and querying the CommandLog table.</p>



<p class="wp-block-paragraph">Now that it has successfully run, you need to publish it. Edit the Runbook, then click Publish.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="319" height="111" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks14.png" alt="This is where Azure Automation and Runbooks come together" class="wp-image-6801" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks14.png 319w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks14-300x104.png 300w" sizes="(max-width: 319px) 100vw, 319px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">Let’s put a schedule on it. Go back to your Azure Automation account. Scroll down again to the Shared Resources section, and select the Schedules page.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="167" height="193" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks15.png" alt="put your automation into a schedule" class="wp-image-6802"/></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">Click on + Add a schedule, then in the New Schedule blade configure the schedule however you would like, including recurrence and frequency.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="297" height="573" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks16.png" alt="Choose a new schedule" class="wp-image-6803" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks16.png 297w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks16-155x300.png 155w" sizes="(max-width: 297px) 100vw, 297px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">Next, click Create, then go back to your Runbook and in the Resources section select Schedules:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="391" height="381" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks17.png" alt="go back to your Runbook and in the Resources section select Schedules" class="wp-image-6804" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks17.png 391w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks17-300x292.png 300w" sizes="(max-width: 391px) 100vw, 391px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">Click on + Add a schedule, then select the option to link a schedule to your runbook.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="377" height="195" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks18.png" alt="Add a schedule, then select the option to link a schedule to your runbook." class="wp-image-6805" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks18.png 377w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/02/AzureRunbooks18-300x155.png 300w" sizes="(max-width: 377px) 100vw, 377px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">From there, select the schedule you created earlier, then click OK.</p>



<p class="wp-block-paragraph"><strong>And there you have it</strong>: you have successfully created an Azure Automation and Runbook to automate your index optimization.</p>



<h3 class="wp-block-heading">Pros and Cons of Azure Automation and Runbooks</h3>



<p class="wp-block-paragraph">Having worked with this for a while, there were some things that I want to highlight in terms of what I liked and some areas that could be better.</p>



<p class="wp-block-paragraph">In the “pros” category, it was super easy to get started and going with it. I had it running in only a few hours, and that was not knowing anything about it when I began this journey a few months ago. I love the flexibility of being able to use PowerShell, Python, or a graphical interface to build the Runbook. Awesome.</p>



<p class="wp-block-paragraph">I also love that I can obfuscate critical values with variables and schedule the Runbook. Sweet. And I can test the Runbook with a UI in the portal prior to publishing and scheduling. Even sweeter.</p>



<p class="wp-block-paragraph">For the downsides, it is “per database.&#8221; Meaning that even though the query says “User Databases” (if you are familiar with Ola’s scripts), it is ONLY going to run the index optimize against the database specified in the parameter. That is just a limitation of Azure, not the Runbook. Thus, if you want to optimize indexes against multiple databases, you need multiple Runbooks. Or, modify the one Runbook to connect to multiple databases. This can get kinda messy.</p>



<p class="wp-block-paragraph">You also need to play with the -Timeout parameter, or not include it at all.</p>



<h3 class="wp-block-heading">Summary</h3>



<p class="wp-block-paragraph">This blog post just touches the surface of what Azure Automation and Runbooks can do. We often see companies not doing index maintenance in Azure because either they don’t know how to do it or it is too difficult. Hopefully this will help get people going.</p>



<p class="wp-block-paragraph">In the next post in this series, I’ll show how to do the same thing using Azure Functions.</p>
<p>The post <a href="https://sqlsolutionsgroup.com/azure-automation-and-runbooks/">How To: Azure Automation and Runbooks</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlsolutionsgroup.com/azure-automation-and-runbooks/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>Run SQL Jobs in Azure: A Prologue</title>
		<link>https://sqlsolutionsgroup.com/run-sql-jobs-in-azure-a-prologue/</link>
					<comments>https://sqlsolutionsgroup.com/run-sql-jobs-in-azure-a-prologue/#comments</comments>
		
		<dc:creator><![CDATA[Scott Klein]]></dc:creator>
		<pubDate>Fri, 20 Jan 2023 18:10:58 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">https://sqlsolutionsgroup.com/?p=6729</guid>

					<description><![CDATA[<p>This is an introduction to an upcoming series of posts dedicated to supporting you and addressing the different ways to not only do index maintenance, but also generally how to run SQL jobs in Azure. I’ll use the topic of index maintenance in Azure as the example. A large trend we have been seeing lately [&#8230;]</p>
<p>The post <a href="https://sqlsolutionsgroup.com/run-sql-jobs-in-azure-a-prologue/">Run SQL Jobs in Azure: A Prologue</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="600" height="400" src="https://sqlsolutionsgroup.com/wp-content/uploads/2023/01/Azure-Sql.jpg" alt="" class="wp-image-6733" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2023/01/Azure-Sql.jpg 600w, https://sqlsolutionsgroup.com/wp-content/uploads/2023/01/Azure-Sql-300x200.jpg 300w" sizes="(max-width: 600px) 100vw, 600px" /><figcaption class="wp-element-caption">Image from Tada Images</figcaption></figure>


<p>This is an introduction to an upcoming series of posts dedicated to supporting you and addressing the different ways to not only do index maintenance, but also generally how to run SQL jobs in Azure. I’ll use the topic of index maintenance in Azure as the example.</p>
<p>A large trend we have been seeing lately is companies and organizations calling us to help improve and troubleshoot their Azure SQL Database performance and related issues. We always begin with a deep analysis, looking at queries, indexes, and configuration settings as part of a top-down, wholistic analysis.</p>
<p>Nine times out of 10, we find that one of the main things that is missing is traditional index maintenance. I believe this is due in large part to a misunderstanding of what Azure actually provides. It could be because the Azure portal has a section for each Azure SQL Database called Intelligent Performance. It provides Performance Recommendations as well as automatic tuning, and has likely led some to believe that Azure automatically does some level of index maintenance (i.e., updating statistics, rebuilding indexes, etc.).</p>
<p>While Azure <em><strong>does</strong></em> do a lot of things for you, maintaining indexes and statistics is not one of them. That is still up to you. The problem is that Microsoft does not currently make doing this easy. But we&#8217;re here to help!</p>
<p>Over the next few blog posts I’ll be covering three main ways to run SQL jobs in Azure:</p>
<ul>
<li>Azure Automation and Runbooks</li>
<li>Azure Functions</li>
<li>Azure Elastic Jobs</li>
</ul>
<p>In each blog post, I’ll discuss the pros and cons of each, provide an example of how to set up and configure them, and what to expect in each each.</p>
<p>While there are certainly other ways to run SQL jobs in Azure, the aforementioned three are the primary “go-to&#8221; options. Join me for this exploration of Azure SQL!</p>
<p></p><p>The post <a href="https://sqlsolutionsgroup.com/run-sql-jobs-in-azure-a-prologue/">Run SQL Jobs in Azure: A Prologue</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlsolutionsgroup.com/run-sql-jobs-in-azure-a-prologue/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>SQL Migration Steps: #1 Why Migrate SQL Server?</title>
		<link>https://sqlsolutionsgroup.com/why-migrate-sql-server/</link>
		
		<dc:creator><![CDATA[Scott Klein]]></dc:creator>
		<pubDate>Tue, 20 Jul 2021 12:33:00 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[#microsftcertifedmaster]]></category>
		<category><![CDATA[#microsftpartner]]></category>
		<category><![CDATA[#SQLAB]]></category>
		<category><![CDATA[#SQlatino]]></category>
		<category><![CDATA[#SQlatinoamerica]]></category>
		<category><![CDATA[#sqldatabase]]></category>
		<category><![CDATA[#sqldeveloper]]></category>
		<category><![CDATA[#SQLgroupie]]></category>
		<category><![CDATA[#sqlimer]]></category>
		<category><![CDATA[#sqlimerbymay]]></category>
		<category><![CDATA[#sqlinjection]]></category>
		<category><![CDATA[#sqlinternals]]></category>
		<category><![CDATA[#sqlite]]></category>
		<category><![CDATA[#sqlite3]]></category>
		<category><![CDATA[#SQLLearning]]></category>
		<category><![CDATA[#SQLMagazine]]></category>
		<category><![CDATA[#sqlmanagementstudio]]></category>
		<category><![CDATA[#sqlmanager]]></category>
		<category><![CDATA[#Sqlmap]]></category>
		<category><![CDATA[#sqlrun]]></category>
		<category><![CDATA[#sqlsaturday2017]]></category>
		<category><![CDATA[#sqlsatvienna]]></category>
		<category><![CDATA[#sqlserver]]></category>
		<category><![CDATA[#SQLserver2012]]></category>
		<category><![CDATA[#sqlserver2014]]></category>
		<category><![CDATA[#sqlserver2017]]></category>
		<category><![CDATA[#sqlserver2022]]></category>
		<category><![CDATA[#SQLServeronLinux]]></category>
		<category><![CDATA[#SQLsolutionsgroup]]></category>
		<category><![CDATA[#SQLTraining]]></category>
		<category><![CDATA[#SQLYog]]></category>
		<category><![CDATA[end of support]]></category>
		<category><![CDATA[migrate]]></category>
		<category><![CDATA[migrations]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[sql server migration]]></category>
		<category><![CDATA[SQLPASS]]></category>
		<category><![CDATA[SQLSaturday]]></category>
		<category><![CDATA[SSG]]></category>
		<category><![CDATA[upgrade]]></category>
		<guid isPermaLink="false">https://sqlsolutionsgroup.com/?p=6462</guid>

					<description><![CDATA[<p>At SSG, our clients run many different versions of SQL Server, even as far back as 2008. This post is for them as well as anyone running SQL Server 2008 or 2012 and who need an answer to the question, &#8220;why migrate SQL Server?&#8221; If you are still running SQL Server 2012, you don’t have [&#8230;]</p>
<p>The post <a href="https://sqlsolutionsgroup.com/why-migrate-sql-server/">SQL Migration Steps: #1 Why Migrate SQL Server?</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">At SSG, our clients run many different versions of SQL Server, even as far back as 2008. This post is for them as well as anyone running SQL Server 2008 or 2012 and who need an answer to the question, &#8220;why migrate SQL Server?&#8221;</p>



<p class="wp-block-paragraph">If you are still running SQL Server 2012, you don’t have much time until Microsoft no longer supports it. Microsoft provides mainstream support for a product for five years, during which time they add features and enhancements (along with bug fixes and security patches) to the product. After that, you typically get another five years of extended support. During this period, Microsoft only provides bug fixes and security patches. With SQL Server 2012, mainstream support ended four years ago (July 2017), and extended support ends July 2022.</p>



<p class="wp-block-paragraph">This post, the first in a series covering several aspects of migration, isn’t about what you should migrate <strong>to</strong>, but, as the title suggests, why you should consider it. We’ll also address <em>upgrade</em> too, because the decision you need to make is: do you migrate (different platform), or upgrade (different version), or both?</p>



<p class="wp-block-paragraph">There are many different reasons we would give as to why migrate SQL Server (or upgrade), but this post will only list a few and focus on the top reasons we see or have discussed with our clients. These are in no particular order.</p>



<h3 class="wp-block-heading">End of Support</h3>



<p class="wp-block-paragraph">Depending on who you are, this is either a big deal, or it isn’t. The old adage “if it ain’t broke, don’t fix it” is still very alive and well in some places. For others, EOS is certainly a big deal because, after extended support ends, you won&#8217;t see any more bug fixes or security patches. With all the hacking and data stealing lately, we highly recommend you don’t run anything that you haven&#8217;t properly secured. Your cyber defenses are down and that makes you extremely vulnerable to threats and cyberattacks.</p>



<h3 class="wp-block-heading">Cost</h3>



<p class="wp-block-paragraph">One of the reasons we hear occasionally for not migrating or upgrading is the cost. In our view, however, you can’t afford <strong>not</strong> to upgrade/migrate. We find, in many cases, it is more costly to maintain existing, aging environments than it is to upgrade or migrate.</p>



<h3 class="wp-block-heading">Performance</h3>



<p class="wp-block-paragraph">Unless you are running super archaic hardware, did you know that you can get tremendous performance boosts and improvements just by upgrading to SQL Server 2016 or later? Microsoft made significant tremendous improvements and enhancements to SQL Server 2016 and later versions. These improvements help SQL Server take advantage of recent hardware architecture changes. If you don’t believe me, here is a <u><a href="https://channel9.msdn.com/Shows/Data-Exposed/SQL-Server-2016-Its-Just-Faster?term=data%20exposed&amp;lang-en=true&amp;pageSize=15&amp;skip=165">video</a></u> I did when I was at Microsoft with the one and only Bob Ward, along with Bob Dorr, which explain the how and why.</p>



<h3 class="wp-block-heading">New Features/Functionality</h3>



<p class="wp-block-paragraph">The “upgrade for the new features” reason is one you have heard a gazillion times, so I won’t spend too much time on this. But, it should be part of your considerations. Simply put, SQL Server 2016 introduced the Query Store, and that alone is worth upgrading for the ability to find query bottlenecks and performance differences caused by query plan changes. There’s a reason it is Azure SQL Database enables bit by default &lt;wink&gt;. And you can’t (or shouldn’t) ignore many of the security and other performance features from post-2012 versions. A quick internet search will show you all the goodness you’re missing.</p>



<h3 class="wp-block-heading">Cloud Benefits</h3>



<p class="wp-block-paragraph">I’ll wrap up with a word on the cloud, and this is one that we wish more companies and individuals would grok. With SQL Server 2012 coming to EOS, one of the targets you should consider is a cloud platform. Yes, I did say this isn’t a “what to migrate to” post, so let me explain <strong>why</strong> you should consider a cloud platform. There are many benefits, and I believe the biggest reasons include, but are not limited to, the opportunity to reduce on-premises infrastructure complexity, increase security, and reduce costs. More and more companies are moving to the cloud to reduce the cost of data management, and the security measures implemented by most cloud vendors are outstanding.</p>



<h3 class="wp-block-heading">Conclusion</h3>



<p class="wp-block-paragraph">We could easily discuss many more items to consider around the topic of why migrate SQL Server. The goal is to touch on the top few and help you get the thought process started. And as always, we are here to help, even if only to discuss some of these reasons in more glorious detail.</p>
<p>The post <a href="https://sqlsolutionsgroup.com/why-migrate-sql-server/">SQL Migration Steps: #1 Why Migrate SQL Server?</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How To: Configure Read-Only Routing for Availability Groups</title>
		<link>https://sqlsolutionsgroup.com/how-to-configure-read-only-routing-for-availability-groups/</link>
					<comments>https://sqlsolutionsgroup.com/how-to-configure-read-only-routing-for-availability-groups/#comments</comments>
		
		<dc:creator><![CDATA[Scott Klein]]></dc:creator>
		<pubDate>Wed, 10 Mar 2021 01:16:16 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[#microsftcertifedmaster]]></category>
		<category><![CDATA[#microsftpartner]]></category>
		<category><![CDATA[#SQLAB]]></category>
		<category><![CDATA[#SQlatino]]></category>
		<category><![CDATA[#SQlatinoamerica]]></category>
		<category><![CDATA[#sqldatabase]]></category>
		<category><![CDATA[#sqldeveloper]]></category>
		<category><![CDATA[#SQLgroupie]]></category>
		<category><![CDATA[#sqlimer]]></category>
		<category><![CDATA[#sqlimerbymay]]></category>
		<category><![CDATA[#sqlinjection]]></category>
		<category><![CDATA[#sqlinternals]]></category>
		<category><![CDATA[#sqlite]]></category>
		<category><![CDATA[#sqlite3]]></category>
		<category><![CDATA[#SQLLearning]]></category>
		<category><![CDATA[#SQLMagazine]]></category>
		<category><![CDATA[#sqlmanagementstudio]]></category>
		<category><![CDATA[#sqlmanager]]></category>
		<category><![CDATA[#Sqlmap]]></category>
		<category><![CDATA[#sqlrun]]></category>
		<category><![CDATA[#sqlsaturday2017]]></category>
		<category><![CDATA[#sqlsatvienna]]></category>
		<category><![CDATA[#sqlserver]]></category>
		<category><![CDATA[#SQLserver2012]]></category>
		<category><![CDATA[#sqlserver2014]]></category>
		<category><![CDATA[#sqlserver2017]]></category>
		<category><![CDATA[#sqlserver2022]]></category>
		<category><![CDATA[#SQLServeronLinux]]></category>
		<category><![CDATA[#SQLsolutionsgroup]]></category>
		<category><![CDATA[#SQLTraining]]></category>
		<category><![CDATA[#SQLYog]]></category>
		<category><![CDATA[AG]]></category>
		<category><![CDATA[availability groups]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQLPASS]]></category>
		<category><![CDATA[SQLSaturday]]></category>
		<category><![CDATA[SSG]]></category>
		<guid isPermaLink="false">https://sqlsolutionsgroup.com/?p=6282</guid>

					<description><![CDATA[<p>At SSG, we work with Always On availability groups (AGs) every day. Most of our clients who have implemented AGs need the high availability that they provide, as well as the ability to offload much of the reporting/read-only workloads to the read-write node. To accomplish this, SQL Server provides the ability to configure read-only routing [&#8230;]</p>
<p>The post <a href="https://sqlsolutionsgroup.com/how-to-configure-read-only-routing-for-availability-groups/">How To: Configure Read-Only Routing for Availability Groups</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">At SSG, we work with Always On availability groups (AGs) every day. Most of our clients who have implemented AGs need the high availability that they provide, as well as the ability to offload much of the reporting/read-only workloads to the read-write node. To accomplish this, SQL Server provides the ability to configure read-only routing in availability groups.</p>



<p class="wp-block-paragraph">With this, you can route connections specified as read-only to a secondary replica configured as a secondary role. Not too long ago, you had to configure read-only routing using T-SQL or PowerShell. Anyone that has done read-only routing using T-SQL knows that it can be a bit tricky to get it right. However, it wasn’t too long ago that Microsoft added the capability to configure read-only routing to the Availability Group Properties dialog.</p>



<p class="wp-block-paragraph">If you’ve been following us lately and reading our recent blog posts, you know that <a href="https://sqlsolutionsgroup.com/skill-up-or-give-up-azure-data-studio-or-bust/" target="_blank" rel="noreferrer noopener"><u>Randy</u></a> and <a href="https://sqlsolutionsgroup.com/ssms-vs-azure-data-studio/" target="_blank" rel="noreferrer noopener"><u>I recently</u></a> took it upon ourselves to only use Azure Data Studio whenever possible. Unfortunately, ADS doesn&#8217;t have an option for working with Always On availability groups. There also isn’t a decent extension out there that does … <em>yet</em>. Hehe. Anyway, for this exercise we turned to the trusty SQL Server Management Studio.</p>



<h3 class="wp-block-heading">Configuring Read-Only Routing</h3>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="810" height="610" class="wp-image-6293" src="https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/1edit-1.png" alt="" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/1edit-1.png 810w, https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/1edit-1-300x226.png 300w, https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/1edit-1-768x578.png 768w" sizes="(max-width: 810px) 100vw, 810px" /></figure>



<p class="wp-block-paragraph">Configuring read-only routing using the UI is quite easy. It simply requires two steps:</p>



<ul class="wp-block-list">
<li>For each readable secondary replica that you want to support read-only routing, specify a read-only routing URL.</li>
<li>For each replica in the AG that you want to support read-only routing when it is the primary replica, specify a read-only routing list.</li>
</ul>



<p class="wp-block-paragraph">In this example, we have two nodes, we’ll call them SQL01 and SQL02. When SQL01 is the primary, we want SQL Server to route all read-only connections to the secondary replica. If the secondary replica isn’t available, send the connections to the primary. To do that, we select the appropriate replicas, in order, from the <em>Available replicas</em> box and move them over to the <em>Read-only routing list</em>.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="933" height="609" class="wp-image-6294" src="https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/2-edit-1.png" alt="" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/2-edit-1.png 933w, https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/2-edit-1-300x196.png 300w, https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/2-edit-1-768x501.png 768w" sizes="(max-width: 933px) 100vw, 933px" /></figure>



<p class="wp-block-paragraph">Thus, we select SQL01 in the top box. Then in the Available Replicas box, we first select SQL02 and add that to the routing list, followed by SQL01. In this way, SQL Server routes all read-only connections to the secondary replica, SQL02. If SQL02 isn’t available, the connections go to SQL01.</p>



<p class="wp-block-paragraph">We repeat the process for SQL02, but in reverse. In the Available Replicas box, we first select SQL01 and add that to the routing list, followed by SQL02. In this way, SQL Server routes all read-only connections to the secondary replica, SQL01. If SQL01 isn’t available, the connections go to SQL02.</p>



<p class="wp-block-paragraph">What I like about this is that when you first open the dialog and select the Read-only routing page, the Routing URL values are already filled in for you. You just need to specify the list. <strong><em>Sweet</em></strong>. After you configure everything, it should look something like the following.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="934" height="609" class="wp-image-6295" src="https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/3edit-1.png" alt="" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/3edit-1.png 934w, https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/3edit-1-300x196.png 300w, https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/3edit-1-768x501.png 768w" sizes="(max-width: 934px) 100vw, 934px" /></figure>



<p class="wp-block-paragraph">You don’t need to add both nodes to the routing list. You can only list the secondary replica in the routing list, but it is nice to have a fallback if the secondary replica isn’t available.</p>



<h3 class="wp-block-heading">One Final Note: T-SQL</h3>



<p class="wp-block-paragraph">If you haven’t done this with T-SQL, there&#8217;s a nifty option here. Once you have configured read-only routing, <strong>before</strong> you click OK, script the actions to a new query window so you can see what the T-SQL looks like:</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="352" height="52" class="wp-image-6286" src="https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/word-image-3.png" alt="" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/word-image-3.png 352w, https://sqlsolutionsgroup.com/wp-content/uploads/2021/03/word-image-3-300x44.png 300w" sizes="(max-width: 352px) 100vw, 352px" /></figure>



<pre class="wp-block-code"><code class="language-sql" lang="sql">USE [master]
 GO
 ALTER AVAILABILITY GROUP [SQLAG01]
 MODIFY REPLICA ON N'SQL01' WITH (PRIMARY_ROLE(READ_ONLY_ROUTING_LIST = (N'SQL02',N'SQL01')))
 GO
 USE [master]
 GO
 ALTER AVAILABILITY GROUP [SQLAG01]
 MODIFY REPLICA ON N'SQL02' WITH (PRIMARY_ROLE(READ_ONLY_ROUTING_LIST = (N'SQL01',N'SQL02')))
 GO</code></pre>



<p class="wp-block-paragraph">&nbsp;</p>
<p>The post <a href="https://sqlsolutionsgroup.com/how-to-configure-read-only-routing-for-availability-groups/">How To: Configure Read-Only Routing for Availability Groups</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlsolutionsgroup.com/how-to-configure-read-only-routing-for-availability-groups/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title>SQL Server Management Studio vs. Azure Data Studio</title>
		<link>https://sqlsolutionsgroup.com/ssms-vs-azure-data-studio/</link>
					<comments>https://sqlsolutionsgroup.com/ssms-vs-azure-data-studio/#comments</comments>
		
		<dc:creator><![CDATA[Scott Klein]]></dc:creator>
		<pubDate>Tue, 23 Feb 2021 10:30:00 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[#microsftcertifedmaster]]></category>
		<category><![CDATA[#microsftpartner]]></category>
		<category><![CDATA[#SQLAB]]></category>
		<category><![CDATA[#SQlatino]]></category>
		<category><![CDATA[#SQlatinoamerica]]></category>
		<category><![CDATA[#sqldatabase]]></category>
		<category><![CDATA[#sqldeveloper]]></category>
		<category><![CDATA[#SQLgroupie]]></category>
		<category><![CDATA[#sqlimer]]></category>
		<category><![CDATA[#sqlimerbymay]]></category>
		<category><![CDATA[#sqlinjection]]></category>
		<category><![CDATA[#sqlinternals]]></category>
		<category><![CDATA[#sqlite]]></category>
		<category><![CDATA[#sqlite3]]></category>
		<category><![CDATA[#SQLLearning]]></category>
		<category><![CDATA[#SQLMagazine]]></category>
		<category><![CDATA[#sqlmanagementstudio]]></category>
		<category><![CDATA[#sqlmanager]]></category>
		<category><![CDATA[#Sqlmap]]></category>
		<category><![CDATA[#sqlrun]]></category>
		<category><![CDATA[#sqlsaturday2017]]></category>
		<category><![CDATA[#sqlsatvienna]]></category>
		<category><![CDATA[#sqlserver]]></category>
		<category><![CDATA[#SQLserver2012]]></category>
		<category><![CDATA[#sqlserver2014]]></category>
		<category><![CDATA[#sqlserver2017]]></category>
		<category><![CDATA[#sqlserver2022]]></category>
		<category><![CDATA[#SQLServeronLinux]]></category>
		<category><![CDATA[#SQLsolutionsgroup]]></category>
		<category><![CDATA[#SQLTraining]]></category>
		<category><![CDATA[#SQLYog]]></category>
		<category><![CDATA[ads]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQLPASS]]></category>
		<category><![CDATA[SQLSaturday]]></category>
		<category><![CDATA[SSG]]></category>
		<category><![CDATA[ssms]]></category>
		<guid isPermaLink="false">https://sqlsolutionsgroup.com/?p=6255</guid>

					<description><![CDATA[<p>The other day I opened SQL Server Management Studio (SSMS) on my laptop. It had been some time since I had opened it (typically I use SSMS installed on the client’s machine), so I got the popup message that there was an update. I downloaded the update and started the install. I was a bit [&#8230;]</p>
<p>The post <a href="https://sqlsolutionsgroup.com/ssms-vs-azure-data-studio/">SQL Server Management Studio vs. Azure Data Studio</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">The other day I opened SQL Server Management Studio (SSMS) on my laptop. It had been some time since I had opened it (typically I use SSMS installed on the client’s machine), so I got the popup message that there was an update. I downloaded the update and started the install. I was a bit surprised that, as part of the SSMS update, I ended up with Azure Data Studio (ADS) as well.</p>



<p class="wp-block-paragraph">As of this writing, the latest version of SSMS is 18.8. ADS has been included with the SSMS install since version 18.7. What annoyed me is that it didn’t give me the option to install it; Microsoft forced it on me. Meaning, if I wanted the latest SSMS, I&#8217;ll also get ADS if I want it or not.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="832" height="472" class="wp-image-6211" src="https://sqlsolutionsgroup.com/wp-content/uploads/2021/02/SSMS-Vs-ADS.jpg" alt="Azure Data Studio" srcset="https://sqlsolutionsgroup.com/wp-content/uploads/2021/02/SSMS-Vs-ADS.jpg 832w, https://sqlsolutionsgroup.com/wp-content/uploads/2021/02/SSMS-Vs-ADS-300x170.jpg 300w, https://sqlsolutionsgroup.com/wp-content/uploads/2021/02/SSMS-Vs-ADS-768x436.jpg 768w" sizes="(max-width: 832px) 100vw, 832px" /></figure>



<p class="wp-block-paragraph">This bothered me for a couple of reasons. First, why is Microsoft forcing ADS on me? That just does not seem right. Second, the last time I used ADS, which was, admittedly, quite some time ago, it was super light on the functionality I need as a DBA. So, after I stewed a bit, I thought I needed to “back the bus up” and think about this a little more — approach ADS from a different angle. I realized I didn’t know enough about ADS to know if it is a tool I can use every day in my role as a DBA.</p>



<h3 class="wp-block-heading">Some Background</h3>



<p class="wp-block-paragraph">It isn’t a secret that, up until a few years ago, SSMS was getting long in the tooth. Meaning, Microsoft updated it only when they updated SQL Server. As such, you had to wait until the next release of SQL Server to get any updates to SSMS. And even then, it was still looking its age.</p>



<p class="wp-block-paragraph">That changed not long ago when Microsoft decided to decouple the SSMS install from the SQL Server install, allowing regular updates for and bug fixes for SSMS. The UI hasn’t changed but at least it’s getting some regular love. However, the winds of change also dictated that we needed something newer and more effective to support both on-premises and the cloud, and across platforms. SSMS is Windows only, and with SQL Server now supporting Linux, it was time for some changes.</p>



<p class="wp-block-paragraph">The answer was Azure Data Studio (initially released in 2018 and known as SQL Operations Studio), a cross-platform tool that, as the Microsoft documentation states, is “for data professionals using on-premises and cloud platforms” that works on Windows, macOS, and Linux. I could not agree more with the cross-platform support, as this is definitely needed. But, I keep getting caught up on the “for database professionals” part.</p>



<p class="wp-block-paragraph">Here’s the thing: If Microsoft is going to force ADS on me, it needs to provide a decent level of DBA coverage in terms of features and functionality, comparable to SSMS. I’m a database professional, and the last time I used ADS, it was missing a lot of the UI features and functionality for managing database environments. Is that still the case? Can I manage availability groups (AGs) or dig into the Query Store? Can I even mouse click on the databases node and select “new database”?</p>



<p class="wp-block-paragraph">I’ll be honest though. The truth is I’ve had ADS installed for a LONG time, and I keep it up to date (just like SSMS, it has monthly updates). But, I rarely use it. So why do I have it installed? I don’t know…probably because somewhere deep in my psyche I want to believe and just haven&#8217;t made the leap.</p>



<h3 class="wp-block-heading">The Challenge</h3>



<p class="wp-block-paragraph">Thus, my quest of recent has been to do a deep analysis of both tools and determine if I am underselling ADS; and, can I be more efficient as a DBA using it? Or, are these for two completely different audiences?</p>



<p class="wp-block-paragraph">I had questions, and I needed answers. Part of the research included talking to one of the PMs on the tooling team and getting some insight. And now the time has come to step up, dig in, and do my due diligence.</p>



<p class="wp-block-paragraph">As Randy mentioned <a href="https://sqlsolutionsgroup.com/skill-up-or-give-up-azure-data-studio-or-bust/"><u>in his latest blog post</u></a>, we&#8217;re going to take the leap and use Azure Data Studio exclusively (or as exclusively as possible) for the next 30 days. In fact, as you read this, we&#8217;ll have already started. I’ll report on my experience in <a href="https://www.eventbrite.com/e/throw-down-ssms-vs-ads-tickets-141920057713"><u>a free webinar on March 17</u></a>. Sign up for the webinar and learn what Randy and I learned over the 30 days. Randy and I are also going to blog about our experiences, so stay tuned for those as well.</p>



<p class="wp-block-paragraph">With the luck of the Irish, all will go well! Join me on the 17<sup>th</sup> and find out who wins this battle.</p>
<p>The post <a href="https://sqlsolutionsgroup.com/ssms-vs-azure-data-studio/">SQL Server Management Studio vs. Azure Data Studio</a> appeared first on <a href="https://sqlsolutionsgroup.com">SQL Solutions Group</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlsolutionsgroup.com/ssms-vs-azure-data-studio/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
	</channel>
</rss>
