<?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>David Barbarin &#187; Azure SQL Database</title>
	<atom:link href="https://blog.developpez.com/mikedavem/ptag/azure-sql-database/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/mikedavem</link>
	<description>MVP DataPlatform - MCM SQL Server</description>
	<lastBuildDate>Thu, 09 Sep 2021 21:19:50 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>Azure monitor as observability platform for Azure SQL Databases and more</title>
		<link>https://blog.developpez.com/mikedavem/p13205/sql-azure/azure-monitor-as-observability-platform-for-azure-sql-databases</link>
		<comments>https://blog.developpez.com/mikedavem/p13205/sql-azure/azure-monitor-as-observability-platform-for-azure-sql-databases#comments</comments>
		<pubDate>Mon, 08 Feb 2021 16:57:26 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[DevOps]]></category>
		<category><![CDATA[SQL Azure]]></category>
		<category><![CDATA[Azure Monitor]]></category>
		<category><![CDATA[Azure SQL Analytics]]></category>
		<category><![CDATA[Azure SQL Database]]></category>
		<category><![CDATA[devops]]></category>
		<category><![CDATA[Log Analytics]]></category>
		<category><![CDATA[observability]]></category>
		<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/mikedavem/?p=1762</guid>
		<description><![CDATA[In a previous blog post, I wrote about reasons we moved our monitoring of on-prem SQL Server instances on Prometheus and Grafana. But what about Cloud and database services? We have different options and obviously in my company we thought &#8230; <a href="https://blog.developpez.com/mikedavem/p13205/sql-azure/azure-monitor-as-observability-platform-for-azure-sql-databases">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>In a previous <a href="https://blog.developpez.com/mikedavem/p13203/sql-server-2014/why-we-moved-sql-server-monitoring-on-prometheus-and-grafana" rel="noopener" target="_blank">blog post</a>, I wrote about reasons we moved our monitoring of on-prem SQL Server instances on Prometheus and Grafana. But what about Cloud and database services? </p>
<p><span id="more-1762"></span></p>
<p>We have different options and obviously in my company we thought first moving our Azure SQL Database workload telemetry on on-prem central monitoring infrastructure as well. But not to mention the main blocker which is the serverless compute tier because Telegraf Server agent would imply initiating a connection that could prevent auto-pausing the database or at least it would made monitoring more complex because it would supposed to have a predictable workload all the time. </p>
<p>The second option was to rely on Azure monitor which is a common platform for combining several logging, monitoring and dashboard solutions across a wide set of Azure resources. It is scalable platform, fully managed and provides a powerful query language and native features like alerts, if logs or metrics match specific conditions. Another important point is there is no vendor lock-in, with this solution, as we can always fallback to our self-hosted Prometheus and Grafana instances if neither computer tier doesn’t fit nor in case Azure Monitor might not be an option anymore! </p>
<p>Firstly, to achieve a good observability with Azure SQL Database we need to put both diagnostic telemetry and SQL Server audits events in a common Log Analytics workspace. A quick illustration below: </p>
<p><a href="http://blog.developpez.com/mikedavem/files/2021/02/173-0-Azure-SQL-DB-Monitor-architecture.jpg"><img src="http://blog.developpez.com/mikedavem/files/2021/02/173-0-Azure-SQL-DB-Monitor-architecture-1024x387.jpg" alt="173 - 0 - Azure SQL DB Monitor architecture" width="584" height="221" class="alignnone size-large wp-image-1763" /></a></p>
<p>Diagnostic settings are configured per database and including basic metrics (CPU, IO, Memory etc …) and also different SQL Server internal metrics as deadlock, blocked processes or query store information about query execution statistic and waits etc&#8230; For more details please refer to the Microsoft <a href="https://docs.microsoft.com/en-us/azure/azure-sql/database/metrics-diagnostic-telemetry-logging-streaming-export-configure?tabs=azure-portal" rel="noopener" target="_blank">BOL</a>.</p>
<p>SQL Azure DB auditing is both server-level or database-level configuration setting. In our context, we defined a template of events at the server level which is then applied to all databases within the logical server. By default, 3 events are automatically audited:<br />
&#8211;	BATCH_COMPLETED_GROUP<br />
&#8211;	SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP<br />
&#8211;	FAILED_DATABASE_AUTHENTICATION_GROUP</p>
<p>The first one of the list is probably to be discussed according to the environment because of its impact but in our context that&rsquo;s ok because we faced a data warehouse workload. However we added other ones to meet our security requirements:<br />
&#8211;	PERMISSION_CHANGE_GROUP<br />
&#8211;	DATABASE_PRINCIPAL_CHANGE_GROUP<br />
&#8211;	DATABASE_ROLE_MEMBER_CHANGE_GROUP<br />
&#8211;	USER_CHANGE_PASSWORD_GROUP</p>
<p>But if you take care about Log Analytics as target for SQL audits, you will notice it is still a feature in preview as shown below:</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2021/02/173-4-Audit-target.jpg"><img src="http://blog.developpez.com/mikedavem/files/2021/02/173-4-Audit-target.jpg" alt="173 - 4 - Audit target" width="484" height="153" class="alignnone size-full wp-image-1765" /></a></p>
<p>To be clear, usually we don’t consider using Azure preview features in production especially when they remain in this state for a long time but in this specific context we got interested by observability capabilities of the platform. From one hand, we get very useful performance insights through SQL Analytics dashboards (again in preview) and from the other hand we can easily query logs and traces through Log Analytics for correlation with other metrics. Obviously, we hope Microsoft moving a step further and providing this feature in GA in the near feature. </p>
<p>Let’s talk briefly of SQL Analytics first. It is an advanced and free cloud monitoring solution for Azure SQL database monitoring performance and it relies mainly on your Azure Diagnostic metrics and Azure Monitor views to present data in a structured way through performance dashboard.</p>
<p>Here an example of built-in dashboards we are using to track activity and high CPU / IO bound queries against our data warehouse.</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2021/02/173-1-SQL-Analytics-general-dashboard-e1612797920282.jpg"><img src="http://blog.developpez.com/mikedavem/files/2021/02/173-1-SQL-Analytics-general-dashboard-1024x410.jpg" alt="173 - 1 - SQL Analytics general dashboard" width="584" height="234" class="alignnone size-large wp-image-1768" /></a></p>
<p>You can use drill-down capabilities to different contextual dashboards to get insights of resource intensive queries. For example, we identified some LOG IO intensive queries against a clustered columnstore index and after some refactoring of UPDATE statement to DELETE + INSERT we reduced drastically LOG IO waits.</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2021/02/173-2-SQL-Analytics-IO-e1612797960660.jpg"><img src="http://blog.developpez.com/mikedavem/files/2021/02/173-2-SQL-Analytics-IO-1024x316.jpg" alt="173 - 2 - SQL Analytics IO" width="584" height="180" class="alignnone size-large wp-image-1767" /></a></p>
<p>In addition, Azure monitor helped us in an another scenario where we tried to figure out recent workload patterns and to know if the current compute tier still fits with it. As said previously, we are relying on Serverless compute tier to handle the data warehouse-oriented workload with both auto-scaling and auto-pausing capabilities. At the first glance, we might expect a typical nightly workload as illustrated to Microsoft <a href="https://docs.microsoft.com/en-us/azure/azure-sql/database/serverless-tier-overview#:~:text=Serverless%20is%20a%20compute%20tier,of%20compute%20used%20per%20second." rel="noopener" target="_blank">BOL</a> and a cost optimized to this workload:</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2021/02/173-6-Serverless-pattern.jpg"><img src="http://blog.developpez.com/mikedavem/files/2021/02/173-6-Serverless-pattern.jpg" alt="173 - 6 - Serverless pattern" width="516" height="316" class="alignnone size-full wp-image-1769" /></a></p>
<p><em>Images from Microsoft BOL</em></p>
<p>It could have been true when the activity started on Azure, but the game has changed with new incoming projects over the time. Starting with the general performance dashboard, the workload seems to follow the right pattern for Serverless compute tier, but we noticed billing keep going during unexpected timeframe as shown below. Let’s precise that I put deliberately only a sample of two days, but this pattern is a good representation of the general workload in our context. </p>
<p><a href="http://blog.developpez.com/mikedavem/files/2021/02/173-3-General-performance-dashboard.jpg"><img src="http://blog.developpez.com/mikedavem/files/2021/02/173-3-General-performance-dashboard-1024x556.jpg" alt="173 - 3 - General performance dashboard" width="584" height="317" class="alignnone size-large wp-image-1771" /></a></p>
<p>Indeed, workload should be mostly nightly-oriented with sporadic activity during the day but quick correlation with other basic metrics like CPU or Memory percentage usage confirmed a persistent activity all day. We have CPU spikes and probably small batches that keep minimum memory around at other moments. </p>
<p>As per the <a href="https://docs.microsoft.com/en-us/azure/azure-sql/database/serverless-tier-overview#:~:text=Serverless%20is%20a%20compute%20tier,of%20compute%20used%20per%20second." rel="noopener" target="_blank">Microsoft documentation</a>, the minimum auto-pausing  delay value is 1h and requires an inactive database (number of sessions = 0 and CPU = 0 for user workload) during this timeframe. Basic metrics didn’t provide any further insights about connections, applications or users that could generate such &laquo;&nbsp;noisy&nbsp;&raquo; activity, so we had to go another way by looking at the SQL Audit logs stored in Azure Monitor Logs. Data can be read through KQL which stands for Kusto Query Language (and not Kibana Query Language <img src="https://blog.developpez.com/mikedavem/wp-includes/images/smilies/icon_smile.gif" alt=":-)" class="wp-smiley" /> ). It’s the language used to query the Azure log databases: Azure Monitor Logs, Azure Monitor Application Insights and others and it is pretty similar to SQL language in the construct. </p>
<p>Here the first query I used to correlate number of events with metrics and that could prevent auto-pausing to kick in for the concerned database including RPC COMPLETED, BATCH COMPLETED, DATABASE AUTHENTICATION SUCCEEDED or DATABASE AUTHENTICATION FAILED</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">AzureDiagnostics<br />
| where Category == 'SQLSecurityAuditEvents' and (action_name_s in ('RPC COMPLETED','BATCH COMPLETED') or action_name_s contains &quot;DATABASE AUTHENTICATION&quot;) &nbsp;and LogicalServerName_s == 'xxxx' and database_name_s == xxxx<br />
| summarize count() by bin(event_time_t, 1h),action_name_s<br />
| render columnchart</div></div>
<p>Results are aggregated and bucketized per hour on generated time event with bin() function. Finally, for a quick and easy read, I choosed a simple and unformatted column chart render. Here the outcome:</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2021/02/173-7-Audit-per-hour-per-event-e1612798279257.jpg"><img src="http://blog.developpez.com/mikedavem/files/2021/02/173-7-Audit-per-hour-per-event-1024x459.jpg" alt="173 - 7 - Audit per hour per event" width="584" height="262" class="alignnone size-large wp-image-1772" /></a></p>
<p>As you probably noticed, daily activity is pretty small compared to nightly one and seems to confirm SQL batches and remote procedure calls. From this unclear picture, we can confirm anyway the daily workload is enough to keep the billing going because there is no per hour timeframe where there is no activity. </p>
<p>Let’s write another KQL query to draw a clearer picture of which applications ran during the a daily timeframe 07:00 – 20:00:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">let start=datetime(&quot;2021-01-26&quot;);<br />
let end=datetime(&quot;2021-01-29&quot;);<br />
let dailystart=7;<br />
let dailyend=20;<br />
let timegrain=1d;<br />
AzureDiagnostics<br />
| project &nbsp;action_name_s, event_time_t, application_name_s, server_principal_name_s, Category, LogicalServerName_s, database_name_s<br />
| where Category == 'SQLSecurityAuditEvents' and (action_name_s in ('RPC COMPLETED','BATCH COMPLETED') or action_name_s contains &quot;DATABASE AUTHENTICATION&quot;) &nbsp;<br />
| where LogicalServerName_s == 'xxxx' and database_name_s == 'xxxx' <br />
| where event_time_t &amp;gt; start and event_time_t &amp;lt; end<br />
| where datetime_part(&amp;quot;Hour&amp;quot;,event_time_t) between (dailystart .. dailyend)<br />
| summarize count() by bin(event_time_t, 1h), application_name_s<br />
| render columnchart with (xtitle = &amp;#039;Date&amp;#039;, ytitle = &amp;#039;Nb events&amp;#039;, title = &amp;#039;Prod SQL Workload pattern&amp;#039;)</div></div>
<p>And here the new outcome:</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2021/02/173-8-Audit-per-hour-per-application.jpg"><img src="http://blog.developpez.com/mikedavem/files/2021/02/173-8-Audit-per-hour-per-application-1024x380.jpg" alt="173 - 8 - Audit per hour per application" width="584" height="217" class="alignnone size-large wp-image-1774" /></a></p>
<p>The new chart reveals some activities from SQL Server Management Studio but most part concerns applications with .Net SQL Data Provider. For a better clarity, we need more information related about applications and, in my context, I managed to address the point by reducing the search scope with the service principal name that issued the related audit event. It results to this new outcome that is pretty similar to previous one:</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2021/02/173-9-Audit-per-hour-per-sp.jpg"><img src="http://blog.developpez.com/mikedavem/files/2021/02/173-9-Audit-per-hour-per-sp-1024x362.jpg" alt="173 - 9 - Audit per hour per sp" width="584" height="206" class="alignnone size-large wp-image-1775" /></a></p>
<p>Good job so far. For a sake of clarity, the service principal obfuscated above is used by our Reporting Server infrastructure and reports to get data from this data warehouse.  By going this way to investigate daily activity at different moments on the concerned Azure SQL database, we came to the conclusion that using Serverless computer tier didn’t make sense anymore and we need to upgrade likely to another computer tier.</p>
<p><strong>Additional thoughts</strong></p>
<p>Azure monitor is definitely a must to have if you are running resources on Azure and if you don’t own a platform for observability (metrics, logs and traces). Otherwise, it can be even beneficial for freeing up your on-prem monitoring infrastructure resources if scalability is a concern. Furthermore, there is no vendor-locking and you can decide to stream Azure monitor data outside in another place but at the cost of additional network transfer fees according to the target scenario. For example, Azure monitor can be used directly as datasource with Grafana. Azure SQL telemetry can be collected with Telegraf agent whereas audit logs can be recorded in another logging system like Kibana. In this blog post, we just surfaced the Azure monitor capabilities but, as demonstrated above, performing deep analysis correlations from different sources in a very few steps is a good point of this platform.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Monitoring Azure SQL Databases with Azure Monitor and Automation</title>
		<link>https://blog.developpez.com/mikedavem/p13198/sql-server-2012/monitoring-azure-sql-databases-with-azure-monitor-and-automation</link>
		<comments>https://blog.developpez.com/mikedavem/p13198/sql-server-2012/monitoring-azure-sql-databases-with-azure-monitor-and-automation#comments</comments>
		<pubDate>Sun, 23 Aug 2020 15:32:07 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[SQL Azure]]></category>
		<category><![CDATA[SQL Server 2012]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Azure Alerts]]></category>
		<category><![CDATA[Azure Monitor]]></category>
		<category><![CDATA[Azure SQL Database]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[monitoring]]></category>
		<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/mikedavem/?p=1653</guid>
		<description><![CDATA[Supervising Cloud Infrastructure is an important aspect of Cloud administration and Azure SQL Databases are no exception. This is something we are continuously improving at my company. On-prem, DBAs often rely on well-established products but with Cloud-based architectures, often implemented &#8230; <a href="https://blog.developpez.com/mikedavem/p13198/sql-server-2012/monitoring-azure-sql-databases-with-azure-monitor-and-automation">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Supervising Cloud Infrastructure is an important aspect of Cloud administration and Azure SQL Databases are no exception. This is something we are continuously improving at my company. </p>
<p>On-prem, DBAs often rely on well-established products but with Cloud-based architectures, often implemented through DevOps projects and developers, monitoring should be been redefined and include some new topics as:</p>
<p><span id="more-1653"></span></p>
<p>1)	Cloud service usage and fees observability<br />
2)	Metrics and events detection that could affect bottom line<br />
3)	Implementing a single platform to report all data that comes from different sources<br />
4)	Trigger rules with data if workload reaches over or drops below certain levels or when an event is enough relevant to not meet the configuration standard and implies unwanted extra billing or when it compromises the company security rules.<br />
5)	Monitoring of the user experience</p>
<p>A key benefit often discussed about Cloud computing, and mainly driven by DevOps, is how it enables agility. One of the meaning of term agility is tied to the rapid provisioning of computer resources (in seconds or minutes) and this shortening provisioning path enables work to start quickly. You may be tempted to grant some provisioning permissions to DEV teams and from my opinion this is not a bad thing, but it may come with some drawbacks if not under control by Ops team including database area. Indeed, for example I have in mind some real cases including architecture configuration drift, security breaches created by unwanted item changes, or idle orphan resources for which you keep being charged. All of these scenarios may lead either to security issues or extra billing and I believe it is important to get clear visibility of such events. </p>
<p>In my company, Azure built-in capabilities with Azure Monitor architecture are our first target (at least in a first stage) and seem to address the aforementioned topics. To set the context, we already relied on Azure Monitor infrastructure for different things including Query Performance Insight, SQL Audit analysis through Log Analytics and Azure alerts for some performance metrics. Therefore, it was the obvious way to go further by adding activity log events to the story. </p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/08/165-1-Azure-Monitor.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/08/165-1-Azure-Monitor.jpg" alt="165 - 1 - Azure Monitor" width="843" height="474" class="alignnone size-full wp-image-1655" /></a></p>
<p>In this blog post, let’s focus on the items 2) 4). I would like to share some experimentations and thoughts about them. As a reminder, items 2) 4) are about catching relevant events to help identifying configuration and security drifts and performing actions accordingly. In addition, as many event-based architectures, additional events may appear or evolve over the time and we started thinking about the concept with the following basic diagram …</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/08/165-2-Workflow-chart-e1598182358607.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/08/165-2-Workflow-chart-e1598182358607.jpg" alt="165 - 2 - Workflow chart" width="800" height="533" class="alignnone size-full wp-image-1657" /></a></p>
<p>… that led to the creation of the two following workflows:<br />
&#8211;	Workflow 1: To get notified immediately for critical events that may compromise security or lead immediately to important extra billing<br />
&#8211;	Workflow 2: To get a report of other misconfigured items (including critical ones) on schedule basis but don’t require quick responsiveness of Ops team.</p>
<p>Concerning the first workflow, using <a href="https://docs.microsoft.com/en-us/azure/azure-monitor/platform/activity-log-alerts" rel="noopener" target="_blank">alerts on activity logs</a>, action groups and webhooks as input of an Azure automation runbook appeared to be a good solution. On another side, the second one only requires running an Azure automation workbook on schedule basis. In fact, this is the same runbook but with different input parameters according to the targeted environment (e.g. PROD / ACC / INT). In addition, the runbook should be able to identity unmanaged events and notified Ops team who will decide either to skip it or to integrate it to runbook processing.</p>
<p>Azure alerts which can be divided in different categories including metric, log alerts and activity log alerts. The last one drew our attention because it allows getting notified for operation of specific resources by email or by generating JSON schema reusable from Azure Automation runbook. Focusing on the latter, we had come up (I believe) with what we thought was a reasonable solution. </p>
<p>Here the high-level picture of the architecture we have implemented:</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/08/165-3-Architecture-e1598182462929.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/08/165-3-Architecture-e1598182462929.jpg" alt="165 - 3 - Architecture" width="800" height="347" class="alignnone size-full wp-image-1659" /></a></p>
<p>1-	During the creation of an Azure SQL Server or a database, corresponding alerts are added with Administrative category with a specific scope. Let&rsquo;s precise that concerned operations must be registered with Azure Resource Manager in order to be used in Activity Log and fortunately they are all including in the <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/resource-provider-operations" rel="noopener" target="_blank">Microsoft.Sql</a> resource provider in this case.<br />
2-	When an event occurs on the targeted environment, an alert is triggered as well as the concerned runbook.<br />
3-	The execution of the same runbook but with different input parameters is scheduled on weekly basis to a general configuration report of our Azure SQL environments.<br />
4-	According the event, Ops team gets notified and acts (either to update misconfigured item, or to delete the unauthorized item, or to update runbook code on Git Repo to handle the new event and so on …)</p>
<p>The skeleton of the Azure automation runbook is pretty similar to the following one:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;height:450px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">[OutputType(&quot;PSAzureOperationResponse&quot;)]<br />
param<br />
(<br />
&nbsp; &nbsp; [Parameter (Mandatory=$false)]<br />
&nbsp; &nbsp; [object] $WebhookData<br />
&nbsp; &nbsp; ,<br />
&nbsp; &nbsp; [parameter(Mandatory=$False)]<br />
&nbsp; &nbsp; [ValidateSet(&quot;PROD&quot;,&quot;ACC&quot;,&quot;INT&quot;)]<br />
&nbsp; &nbsp; [String]$EnvTarget<br />
&nbsp; &nbsp; ,<br />
&nbsp; &nbsp; [parameter(Mandatory=$False)]<br />
&nbsp; &nbsp; [Boolean]$DebugMode = $False<br />
)<br />
<br />
<br />
<br />
<br />
If ($WebhookData)<br />
{<br />
<br />
&nbsp; &nbsp; # Logic to allow for testing in test pane<br />
&nbsp; &nbsp; If (-Not $WebhookData.RequestBody){<br />
&nbsp; &nbsp; &nbsp; &nbsp; $WebhookData = (ConvertFrom-Json -InputObject $WebhookData)<br />
&nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; $WebhookBody = (ConvertFrom-Json -InputObject $WebhookData.RequestBody)<br />
<br />
&nbsp; &nbsp; $schemaId = $WebhookBody.schemaId<br />
<br />
&nbsp; &nbsp; If ($schemaId -eq &quot;azureMonitorCommonAlertSchema&quot;) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; # This is the common Metric Alert schema (released March 2019)<br />
&nbsp; &nbsp; &nbsp; &nbsp; $Essentials = [object] ($WebhookBody.data).essentials<br />
&nbsp; &nbsp; &nbsp; &nbsp; # Get the first target only as this script doesn't handle multiple<br />
&nbsp; &nbsp; &nbsp; &nbsp; $status = $Essentials.monitorCondition<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; # Focus only on succeeded or Fired Events<br />
&nbsp; &nbsp; &nbsp; &nbsp; If ($status -eq &quot;Succeeded&quot; -Or $Status -eq &quot;Fired&quot;)<br />
&nbsp; &nbsp; &nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Extract info from webook <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $alertTargetIdArray = (($Essentials.alertTargetIds)[0]).Split(&quot;/&quot;)<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $SubId = ($alertTargetIdArray)[2]<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $ResourceGroupName = ($alertTargetIdArray)[4]<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $ResourceType = ($alertTargetIdArray)[6] + &quot;/&quot; + ($alertTargetIdArray)[7]<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Determine code path depending on the resourceType<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ($ResourceType -eq &quot;microsoft.sql/servers&quot;)<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # DEBUG<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Write-Output &quot;This is a SQL Server Resource.&quot;<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $firedDate = $Essentials.firedDateTime<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $AlertContext = [object] ($WebhookBody.data).alertContext<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $channel = $AlertContext.channels<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $EventSource = $AlertContext.eventSource<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $Level = $AlertContext.level<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $Operation = $AlertContext.operationName<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $Properties = [object] ($WebhookBody.data).alertContext.properties<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $EventName = $Properties.eventName<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $EventStatus = $Properties.status<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $Description = $Properties.description_scrubbed<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $Caller = $Properties.caller<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $IPAddress = $Properties.ipAddress<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $ResourceName = ($alertTargetIdArray)[8]<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $DatabaseName = ($alertTargetIdArray)[10]<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $Operation_detail = $Operation.Split('/')<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Check firewall rules<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; If ($EventName -eq 'OverwriteFirewallRules'){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Write-Output &quot;Firewall Overwrite is detected ...&quot;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Code to handle firewall update event<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Update DB =&amp;gt; No need to be monitored in real time<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Elseif ($EventName -eq 'UpdateDatabase') {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Code to handle Database config update event or skip <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Create DB<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Elseif ($EventName -eq 'CreateDatabase' -Or `<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $Operation -eq 'Microsoft.Sql/servers/databases/write'){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Write-Output &quot;Azure Database creation has been detected ...&quot;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Code to handle Database creation event or skip <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Delete DB<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Elseif ($EventName -eq 'DeleteDatabase' -Or `<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $Operation -eq 'Microsoft.Sql/servers/databases/delete') {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Write-Output &quot;Azure Database has been deleted ...&quot;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Code to handle Database deletion event or skip <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Elseif ($Operation -eq 'Microsoft.Sql/servers/databases/transparentDataEncryption/write') {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Write-Output &quot;Azure Database Encryption update has been detected ...&quot;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Code to handle Database encryption update event or skip <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Elseif ($Operation -eq 'Microsoft.Sql/servers/databases/auditingSettings/write') {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Write-Output &quot;Azure Database Audit update has been detected ...&quot;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Code to handle Database audit update event or skip <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Elseif ($Operation -eq 'Microsoft.Sql/servers/databases/securityAlertPolicies/write' -or $Operation -eq 'Microsoft.Sql/servers/databases/vulnerabilityAssessments/write') {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Write-Output &quot;Azure ADS update has been detected ...&quot;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Code to handle ADS update event or skip <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ElseIf ($Operation -eq 'Microsoft.Sql/servers/databases/backupShortTermRetentionPolicies/write'){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Write-Output &quot;Azure Retention Backup has been modified ...&quot;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Code to handle Database retention backup update event or skip <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # ... other ones <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Else {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Write-Output &quot;Event not managed yet &nbsp; &nbsp;&quot;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # ResourceType not supported<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Write-Error &quot;$ResourceType is not a supported resource type for this runbook.&quot;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; Else {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # The alert status was not 'Activated' or 'Fired' so no action taken<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Write-Verbose (&quot;No action taken. Alert status: &quot; + $status) -Verbose<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; }<br />
&nbsp; &nbsp; Else{<br />
&nbsp; &nbsp; &nbsp; &nbsp;# SchemaID doesn't correspond to azureMonitorCommonAlertSchema =&amp;gt;&amp;gt; Skip<br />
&nbsp; &nbsp; &nbsp; &nbsp;Write-Host &quot;Skip ...&quot; <br />
&nbsp; &nbsp; }<br />
}<br />
Else {<br />
&nbsp; &nbsp; Write-Output &quot;No Webhook detected ... switch to normal mode ...&quot;<br />
<br />
&nbsp; &nbsp; If ([String]::IsNullOrEmpty($EnvTarget)){<br />
&nbsp; &nbsp; &nbsp; &nbsp; Write-Error '$EnvTarget is mandatory in normal mode'<br />
&nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; #########################################################<br />
&nbsp; &nbsp; # Code for a complete check of Azure SQL DB environment #<br />
&nbsp; &nbsp; #########################################################<br />
}</div></div>
<p>Some comments about the PowerShell script:</p>
<p>1)	Input parameters should include either the Webhook data or specific parameter values for a complete Azure SQL DB check.<br />
2)	The first section should include your own functions to respond to different events. In our context, currently we drew on <a href="https://github.com/sqlcollaborative/dbachecks" rel="noopener" target="_blank">DBAChecks</a> thinking to develop a derived model but why not using directly DBAChecks in a near future?<br />
3)	When an event is triggered, a JSON schema is generated and provides insight. The point here is you must navigate through different properties according to the operation type (cf. <a href="https://docs.microsoft.com/en-us/azure/azure-monitor/platform/activity-log-schema" rel="noopener" target="_blank">BOL</a>).<br />
4)	The increase of events to manage could be a potential issue making the runbook fat especially if we keep both the core functions and event processing. To mitigate this topic, we are thinking to move functions into modules in Azure automation (next step).</p>
<p><strong>Bottom line</strong></p>
<p>Thanks to Azure built-in capabilities we improved our visibility of events that occur on the Azure SQL environment (both expected and unexcepted) and we’re now able to act accordingly. But I should tell you that going this way is not a free lunch and we achieved a reasonable solution after some programming and testing efforts. If you can invest time, it is probably the kind of solution you can add to your study.</p>
<p>See you</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AAD user creation on behalf AAD Service Principal with Azure SQL DB</title>
		<link>https://blog.developpez.com/mikedavem/p13197/sql-azure/aad-user-creation-on-behalf-aad-service-principal-with-azure-sql-db</link>
		<comments>https://blog.developpez.com/mikedavem/p13197/sql-azure/aad-user-creation-on-behalf-aad-service-principal-with-azure-sql-db#comments</comments>
		<pubDate>Sun, 02 Aug 2020 22:28:06 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[DevOps]]></category>
		<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[SQL Azure]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Azure Automation]]></category>
		<category><![CDATA[Azure SQL Database]]></category>
		<category><![CDATA[Azure SQL DB]]></category>
		<category><![CDATA[Powershell]]></category>
		<category><![CDATA[Runbook]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Service Principal]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[System managed identity]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/mikedavem/?p=1643</guid>
		<description><![CDATA[An interesting improvement was announced by the SQL AAD team on Monday 27th July 2020 and concerns the support for Azure AD user creation on behalf of Azure AD Applications for Azure SQL as mentioned to this Microsoft blog post. &#8230; <a href="https://blog.developpez.com/mikedavem/p13197/sql-azure/aad-user-creation-on-behalf-aad-service-principal-with-azure-sql-db">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>An interesting improvement was announced by the SQL AAD team on Monday 27th July 2020 and concerns the support for Azure AD user creation on behalf of Azure AD Applications for Azure SQL as mentioned to this <a href="https://techcommunity.microsoft.com/t5/azure-sql-database/support-for-azure-ad-user-creation-on-behalf-of-azure-ad/ba-p/1491121" rel="noopener" target="_blank">Microsoft blog post</a>. </p>
<p><span id="more-1643"></span></p>
<p>In my company, this is something we were looking for a while with our database refresh process in Azure. Before talking this new feature, let me share a brief history of different considerations we had for this DB refresh process over the time with different approaches we went through. First let’s precise DB Refresh includes usually at least two steps: restoring backup / copying database – you have both ways in Azure SQL Database – and realigning security context with specific users regarding your targeted environment (ACC / INT …).  But the latter is not as trivial as you may expect if you opted to use either a SQL Login / User or a Service Principal to carry out this operation in your process. Indeed, in both cases creating an Azure AD User or Group is not supported, and if you try you will face this error message:</p>
<blockquote><p>‘’ is not a valid login or you do not have permission. </p></blockquote>
<p>All the stuff (either Azure automation runbook and PowerShell modules on-prem) done so far and described afterwards meets the same following process:</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/08/164-1-DB-Refresh-process-e1596406580306.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/08/164-1-DB-Refresh-process-e1596406580306.jpg" alt="164 - 1 - DB Refresh process" width="800" height="566" class="alignnone size-full wp-image-1645" /></a></p>
<p>First, we used Invoke-SQCMD in Azure Automation runbook with T-SQL query to create a copy of a source database to the target server. T-SQL is mandatory in this case as per <a href="https://docs.microsoft.com/en-us/azure/azure-sql/database/database-copy?tabs=azure-powershell" rel="noopener" target="_blank">documented</a> in the Microsoft BOL because PROD and ACC or INT servers are not on the same subscription. Here a simplified sample of code:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">...<br />
$CopyDBCMD = @{<br />
&nbsp; &nbsp; 'Database' = 'master'<br />
&nbsp; &nbsp; 'ServerInstance' = $TargetServerName<br />
&nbsp; &nbsp; 'Username' = $SQLUser<br />
&nbsp; &nbsp; 'Password' = $SQLPWD<br />
&nbsp; &nbsp; 'Query' = 'CREATE DATABASE '+ '[' + $DatabaseName + '] ' + 'AS COPY OF ' + '[' + $SourceServerName + '].[' + $DatabaseName + ']'<br />
} <br />
<br />
Invoke-Sqlcmd @CopyDBCMD <br />
...</div></div>
<p>But as you likely know, Invoke-SQLCMD doesn’t support AAD authentication and because SQL Login authentication was the only option here, it led us dealing with an annoying issue about the security configuration step with AAD users or groups as you may imagine. </p>
<p>Then, because we based authentication mainly on trust architecture and our security rules require using it including apps with managed identities or service principals, we wanted also to introduce this concept to our database refresh process. Fortunately, service principals are supported with <a href="https://techcommunity.microsoft.com/t5/azure-sql-database/token-based-authentication-support-for-azure-sql-db-using-azure/ba-p/386091" rel="noopener" target="_blank">Azure SQL DBs since v12</a> with access token for authentication by ADALSQL. The corresponding DLL is required on your server or if you use it from Azure Automation like us, we added the ADAL.PS module but be aware it is now deprecated, and I advise you to strongly invest in moving to MSAL. Here a sample we used:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;height:450px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">...<br />
$response = Get-ADALToken `<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -ClientId $clientId `<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -ClientSecret $clientSecret `<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -Resource $resourceUri `<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -Authority $authorityUri `<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -TenantId $tenantName<br />
<br />
...<br />
<br />
$connectionString = &quot;Server=tcp:$SqlInstanceFQDN,1433;Initial Catalog=master;Persist Security Info=False;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;&quot;<br />
# Create the connection object<br />
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)<br />
# Set AAD generated token to SQL connection token<br />
$connection.AccessToken = $response.AccessToken<br />
<br />
Try {<br />
&nbsp; &nbsp; $connection.Open()<br />
&nbsp; &nbsp; ...<br />
} <br />
...</div></div>
<p>But again, even if the copy or restore steps are well managed, we still got stuck with security reconfiguration, because service principals were not supported for creating AAD users or groups so far &#8230;</p>
<p>In the meantime, we found out a temporary and interesting solution based on <a href="https://dbatools.io/" rel="noopener" target="_blank">dbatools framework</a> and the <a href="https://docs.dbatools.io/#Invoke-DbaQuery" rel="noopener" target="_blank">Invoke-dbaquery command</a> which supports AAD authentication (Login + Password). As we may not rely on service principal in this case, using a dedicated AAD account was an acceptable tradeoff to manage all the database refresh process steps. But going through this way comes with some disadvantages because running Invoke-dbaquery in a full Azure automation mode is not possible with missing ADALsql.dll. Workaround may be to use hybrid-worker, but we didn’t want to add complexity to our current architecture only for this special case. Instead we decided to move the logic of the Azure automation runbook into on-prem PowerShell framework which already include logic for DB refresh for on-prem SQL Server instances. </p>
<p>Here a simplified sample of code we are using:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;height:450px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">...<br />
Try {<br />
&nbsp; &nbsp; # Connect to get access to Key Vault info<br />
&nbsp; &nbsp; Connect-AzAccount | Out-Null<br />
<br />
&nbsp; &nbsp; [String]$user = (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-SQLBCKUSER&quot;).SecretValueText<br />
&nbsp; &nbsp; [System.Security.SecureString]$pwd = &nbsp;ConvertTo-SecureString (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-SQLBCKPWD&quot;).SecretValueText -AsPlainText -Force<br />
&nbsp; &nbsp; [String]$SourceServerName = (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-NAME&quot;).SecretValueText<br />
&nbsp; &nbsp; [String]$TargetServerName = (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-TARGETNAME&quot;).SecretValueText + '.database.windows.net'<br />
<br />
&nbsp; &nbsp; # DB Restore will be performed in the context of dedicated AAD account <br />
&nbsp; &nbsp; $pscredential = New-Object -TypeName System.Management.Automation.PSCredential($user, $pwd)<br />
<br />
&nbsp; &nbsp; Write-Host &quot;Restoring DB:$DatabaseName from Source Server: $SourceServerName to Target Server: $TargetServerName&quot;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; $Query = &quot;CREATE DATABASE [$DatabaseName] AS COPY OF [$SourceServerName].[$DatabaseName]&quot;<br />
&nbsp; &nbsp; Invoke-DbaQuery `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -SqlInstance $TargetServerName `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -Database master `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -SqlCredential $pscredential `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -Query $Query `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -EnableException <br />
<br />
&nbsp; &nbsp; # Wait for DB online and ready ... <br />
&nbsp; &nbsp; # Code should be implemented for this check <br />
<br />
<br />
&nbsp; &nbsp; Write-Output &quot;Applying security configuration to DB: $DatabaseName on Server:$TargetServerName&quot;<br />
<br />
&nbsp; &nbsp; $Query = &quot;<br />
&nbsp; &nbsp; &nbsp; &nbsp; DROP USER [az_sql_ro];CREATE USER [az_sql_ro] FROM EXTERNAL PROVIDER;<br />
&nbsp; &nbsp; &quot;<br />
&nbsp; &nbsp; Invoke-DbaQuery `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -SqlInstance $TargetServerName `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -Database $DatabaseName `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -SqlCredential $pscredential `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -Query $Query `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -EnableException<br />
<br />
}<br />
Catch {<br />
&nbsp; &nbsp; Write-Host &quot;Error encountered: $($_.Exception.Message)&quot;<br />
} <br />
...</div></div>
<p>Referring to the PowerShell code above, in the second step, we create an AAG group [az_sql_ro] on behalf of the AAD dedicated account with the CLAUSE FROM EXTERNAL PROVIDER. </p>
<p>Finally, with the latest news published by the SQL AAD team, we will likely consider using back service principal instead of dedicated Windows AAD account. <a href="https://techcommunity.microsoft.com/t5/azure-sql-database/support-for-azure-ad-user-creation-on-behalf-of-azure-ad/ba-p/1491121" rel="noopener" target="_blank">This Microsoft blog post</a> explains in details how it works and what you have to setup to make it work correctly. I don’t want to duplicate what is already explained so I will apply the new stuff to my context. </p>
<p>Referring to the above blog post, you need first to setup a server identity for your Azure SQL Server as below:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Set-AzSqlServer `<br />
&nbsp; &nbsp; -ResourceGroupName sandox-rg `<br />
&nbsp; &nbsp; -ServerName a-s-sql02 `<br />
&nbsp; &nbsp; -AssignIdentity<br />
<br />
# Check server identity<br />
Get-AzSqlServer `<br />
&nbsp; &nbsp; -ResourceGroupName sandox-rg `<br />
&nbsp; &nbsp; -ServerName a-s-sql02 | `<br />
&nbsp; &nbsp; Select-Object ServerName, Identity</div></div>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">ServerName Identity &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
---------- -------- &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
a-s-sql02 &nbsp;Microsoft.Azure.Management.Sql.Models.ResourceIdentity</div></div>
<p>Let&rsquo;s have a look at the server identity</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"># Get identity details<br />
$identity = Get-AzSqlServer `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -ResourceGroupName sandox-rg `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -ServerName a-s-sql02<br />
<br />
$identity.identity</div></div>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">PrincipalId &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Type &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; TenantId &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
----------- &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;---- &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -------- &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
7f0d16f7-b172-4c97-94d3-34f0f7ed93cf SystemAssigned 2fcd19a7-ab24-4aef-802b-6851ef5d1ed5</div></div>
<p>In fact, assigning a server identity means creating a system assigned managed identity in the Azure AD tenant that&rsquo;s trusted by the subscription of the instance. To keep things simple, let’s say that System Managed Identity in Azure is like to Managed Account or Group Managed Account on-prem. Those identities are self-managed by the system. Then you need to grant this identity the Azure AD &laquo;&nbsp;Directory Readers &laquo;&nbsp;permission to get rights for creating AAD Users or Groups on behalf of this identity. A PowerShell script is provided by Microsoft <a href="https://docs.microsoft.com/en-us/azure/azure-sql/database/authentication-aad-service-principal-tutorial" rel="noopener" target="_blank">here</a> a sample of code I applied in my context for testing:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;height:450px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">...<br />
Try {<br />
&nbsp; &nbsp; $DatabaseName = &quot;test-DBA&quot; &nbsp; <br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; # Connect to get access to Key Vault info<br />
&nbsp; &nbsp; Connect-AzAccount | Out-Null<br />
<br />
&nbsp; &nbsp; [String]$user = (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-SQLBCKAPPID&quot;).SecretValueText<br />
&nbsp; &nbsp; [System.Security.SecureString]$pwd = &nbsp;ConvertTo-SecureString (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-SQLBCKAPPSECRET&quot;).SecretValueText -AsPlainText -Force<br />
&nbsp; &nbsp; [String]$SourceServerName = (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-NAME&quot;).SecretValueText<br />
&nbsp; &nbsp; [String]$TargetServerName = (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-TARGETNAME&quot;).SecretValueText + '.database.windows.net'<br />
<br />
&nbsp; &nbsp; # DB Restore will be performed in the context of dedicated AAD account <br />
&nbsp; &nbsp; $pscredential = New-Object -TypeName System.Management.Automation.PSCredential($user, $pwd)<br />
<br />
&nbsp; &nbsp; $adalPath &nbsp;= &quot;${env:ProgramFiles}\WindowsPowerShell\Modules\Az.Profile.7.0\PreloadAssemblies&quot;<br />
&nbsp; &nbsp; # To install the latest AzureRM.profile version execute &nbsp;-Install-Module -Name AzureRM.profile<br />
&nbsp; &nbsp; $adal &nbsp; &nbsp; &nbsp;= &quot;$adalPath\Microsoft.IdentityModel.Clients.ActiveDirectory.dll&quot;<br />
&nbsp; &nbsp; $adalforms = &quot;$adalPath\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll&quot;<br />
&nbsp; &nbsp; [System.Reflection.Assembly]::LoadFrom($adal) | Out-Null<br />
&nbsp; &nbsp; $resourceAppIdURI = 'https://database.windows.net/'<br />
<br />
&nbsp; &nbsp; # Set Authority to Azure AD Tenant<br />
&nbsp; &nbsp; $authority = 'https://login.windows.net/' + $tenantId<br />
<br />
&nbsp; &nbsp; $ClientCred = [Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential]::new($clientId, $clientSecret)<br />
&nbsp; &nbsp; $authContext = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new($authority)<br />
&nbsp; &nbsp; $authResult = $authContext.AcquireTokenAsync($resourceAppIdURI,$ClientCred)<br />
&nbsp; &nbsp; $Tok = $authResult.Result.CreateAuthorizationHeader()<br />
&nbsp; &nbsp; $Tok=$Tok.Replace(&quot;Bearer &quot;,&quot;&quot;)<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; Write-host &quot;Token generated is ...&quot;<br />
&nbsp; &nbsp; $Tok<br />
&nbsp; &nbsp; Write-host &nbsp;&quot;&quot;<br />
<br />
&nbsp; &nbsp; Write-Host &quot;Create SQL connectionstring&quot;<br />
&nbsp; &nbsp; $conn = New-Object System.Data.SqlClient.SQLConnection <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; $conn.ConnectionString = &quot;Data Source=$TargetServerName;Initial Catalog=master;Connect Timeout=30&quot;<br />
&nbsp; &nbsp; $conn.AccessToken = $Tok<br />
<br />
&nbsp; &nbsp; Write-host &quot;Connect to database and execute SQL script&quot;<br />
&nbsp; &nbsp; $conn.Open() <br />
<br />
&nbsp; &nbsp; Write-Host &quot;Check connected user ...&quot;<br />
&nbsp; &nbsp; $Query = &quot;SELECT USER_NAME() AS [user_name];&quot;<br />
&nbsp; &nbsp; $command = New-Object -TypeName System.Data.SqlClient.SqlCommand($Query, $conn)<br />
&nbsp; &nbsp; $Command.ExecuteScalar()<br />
&nbsp; &nbsp; $conn.Close()<br />
<br />
&nbsp; &nbsp; Write-Host &quot;Restoring DB:$DatabaseName from Source Server: $SourceServerName to Target Server: $TargetServerName&quot;<br />
<br />
&nbsp; &nbsp; $conn.ConnectionString = &quot;Data Source=$TargetServerName;Initial Catalog=master;Connect Timeout=30&quot;<br />
&nbsp; &nbsp; $conn.AccessToken = $Tok<br />
&nbsp; &nbsp; $conn.Open()<br />
&nbsp; &nbsp; $Query = &quot;DROP DATABASE IF EXISTS [$DatabaseName]; CREATE DATABASE [$DatabaseName] AS COPY OF [$SourceServerName].[$DatabaseName]&quot;<br />
&nbsp; &nbsp; $command = New-Object -TypeName System.Data.SqlClient.SqlCommand($Query, $conn)<br />
&nbsp; &nbsp; $command.CommandTimeout = 1200<br />
&nbsp; &nbsp; $command.ExecuteNonQuery()<br />
&nbsp; &nbsp; $conn.Close()<br />
<br />
&nbsp; &nbsp; # Wait for DB online and ready ... <br />
&nbsp; &nbsp; # Code should be implemented for this check <br />
<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; Write-Output &quot;Applying security configuration to DB: $DatabaseName on Server:$TargetServerName&quot;<br />
<br />
&nbsp; &nbsp; $conn.ConnectionString = &quot;Data Source=$TargetServerName;Initial Catalog=$DatabaseName;Connect Timeout=30&quot;<br />
&nbsp; &nbsp; $conn.AccessToken = $Tok<br />
&nbsp; &nbsp; $conn.Open() <br />
&nbsp; &nbsp; $Query = 'CREATE USER [az_sql_ro] FROM EXTERNAL PROVIDER;'<br />
&nbsp; &nbsp; $command = New-Object -TypeName System.Data.SqlClient.SqlCommand($Query, $conn) &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; $command.ExecuteNonQuery()<br />
&nbsp; &nbsp; $conn.Close()<br />
<br />
}<br />
Catch {<br />
&nbsp; &nbsp; Write-Output &quot;Error encountered: $($_.Exception.Message)&quot;<br />
} <br />
...</div></div>
<p>Using service principal required few changes in my case. I now get credentials of the service principal (ClientId and Secret) from Azure Key Vault instead of the AAD dedicated account used in previous example. I also changed the way to connect to SQL Server by relying on ADALSQL to get the access token instead of using dbatools commands. Indeed, as far as I know, dbatools doesn’t support this authentication way (yet?). </p>
<p>The authentication process becomes as follows:</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/08/164-3-new-auth-process-e1596407082747.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/08/164-3-new-auth-process-e1596407082747.jpg" alt="164 - 3 - new auth process" width="800" height="610" class="alignnone size-full wp-image-1647" /></a></p>
<p>My first test seems to be relevant:</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/08/164-4-test-with-SP-e1596407153885.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/08/164-4-test-with-SP-e1596407153885.jpg" alt="164 - 4 - test with SP" width="800" height="301" class="alignnone size-full wp-image-1648" /></a></p>
<p>This improvement looks promise and may cover broader scenarios as the one I described in this blog post. This feature is in preview at the moment of this write-up and I hope to see it coming soon in GA as well as a potential support of preferred PowerShell framework DBAtools <img src="https://blog.developpez.com/mikedavem/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley" /></p>
<p>See you!</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
