<?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>Azure Archives - Albert Nogués</title>
	<atom:link href="https://www.albertnogues.com/category/cloud/azure/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.albertnogues.com/category/cloud/azure/</link>
	<description>Data and Cloud Freelancer</description>
	<lastBuildDate>Fri, 31 May 2024 11:17:08 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.albertnogues.com/wp-content/uploads/2020/12/cropped-cropped-AlbertLogo2-32x32.png</url>
	<title>Azure Archives - Albert Nogués</title>
	<link>https://www.albertnogues.com/category/cloud/azure/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Data Quality Checks with Soda-Core in Databricks</title>
		<link>https://www.albertnogues.com/data-quality-checks-with-soda-core-in-databricks/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=data-quality-checks-with-soda-core-in-databricks</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Fri, 31 May 2024 11:17:06 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Databricks]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[data quality]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[soda]]></category>
		<category><![CDATA[spark]]></category>
		<category><![CDATA[sql]]></category>
		<guid isPermaLink="false">https://www.albertnogues.com/?p=3439</guid>

					<description><![CDATA[<p>It&#8217;s easy to do data quality checks when working with spark with the soda-core library. The library has support for spark dataframes. I&#8217;ve tested it within a databricks environment and it worked quite easily for me. For the examples of this article i am loading the customers table from the tpch delta tables in the &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/data-quality-checks-with-soda-core-in-databricks/">Data Quality Checks with Soda-Core in Databricks</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>It&#8217;s easy to do data quality checks when working with spark with the soda-core library. The library has support for spark dataframes. I&#8217;ve tested it within a databricks environment and it worked quite easily for me.</p>



<p>For the examples of this article i am loading the customers table from the tpch delta tables in the databricks-datasets folder.</p>



<p>First of all we need to install the library either scoped to our Databricks notebook or on our cluster. In my case i will install it notebook scoped:</p>



<pre class="wp-block-code"><code>%pip install soda-core-spark-df</code></pre>



<p>Then we create a dataframe from the tpch customers table:</p>



<pre class="wp-block-code"><code>#We create a table and read it into a dataframe
customer_df = spark.read.table("delta.`/databricks-datasets/tpch/delta-001/customer/`")</code></pre>



<p>We create a temporary view for our dataframe so soda can query the data and run the checks:</p>



<pre class="wp-block-code"><code>#We create a TempView
customer_df.createOrReplaceTempView("customer")</code></pre>



<p>And here it comes the whole soda core. We will define the checks using yaml syntax:</p>



<pre class="wp-block-code"><code>from soda.scan import Scan
scan = Scan()
scan.set_scan_definition_name("Databricks Test Notebook")
scan.set_data_source_name("customer")
scan.add_spark_session(spark, data_source_name="customer")
#YAML Format
checks = '''
checks for customer:
  - row_count > 0
  - invalid_percent(c_phone) = 0:
      valid regex: ^&#91;0-9]{2}&#91;-]&#91;0-9]{3}&#91;-]&#91;0-9]{3}&#91;-]&#91;0-9]{4}$
  - duplicate_count(c_phone) = 0:
      name: No duplicate phone numbers
  - invalid_count(c_mktsegment) = 0:
      invalid values: &#91;HOUSEHOLD]
      name: HOUSEHOLD is not allowed as a Market Segment
'''
# you can use add_sodacl_yaml_file(s). Useful if the tests are in a github repo or FS
scan.add_sodacl_yaml_str(checks)
scan.execute()
print(scan.get_logs_text())</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img fetchpriority="high" decoding="async" width="570" height="322" src="https://www.albertnogues.com/wp-content/uploads/2024/05/Output1.png" alt="" class="wp-image-3440" srcset="https://www.albertnogues.com/wp-content/uploads/2024/05/Output1.png 570w, https://www.albertnogues.com/wp-content/uploads/2024/05/Output1-300x169.png 300w, https://www.albertnogues.com/wp-content/uploads/2024/05/Output1-106x60.png 106w" sizes="(max-width: 570px) 100vw, 570px" /></figure>
</div>


<p>More info: <a href="https://docs.soda.io/soda/quick-start-databricks.html">Add Soda to a Databricks notebook | Soda Documentation</a></p>



<p>List of validations: <a href="https://docs.soda.io/soda-cl/validity-metrics.html">Validity metrics | Soda Documentation</a> and <a href="https://docs.soda.io/soda-cl/metrics-and-checks.html">SodaCL metrics and checks | Soda Documentation</a></p>



<p>We can somewhat enhance it and generate a Spark Dataframe all out of the list of our warnings or error validation checks:</p>



<pre class="wp-block-code"><code>from datetime import datetime
schema_checks = 'datasource STRING, table STRING, rule_name STRING, rule STRING, column STRING, check_status STRING, number_of_errors_in_sample INT, check_time TIMESTAMP'
list_of_checks = &#91;]
for c in scan.get_scan_results()&#91;'checks']:
    list_of_checks = list_of_checks + &#91;&#91;scan.get_scan_results()&#91;'defaultDataSource'], c&#91;'table'], c&#91;'name'], c&#91;'definition'], c&#91;'column'], c&#91;'outcome'], 0 if 'pass'in c&#91;'outcome'] else int(c&#91;'diagnostics']&#91;'blocks']&#91;0]&#91;'totalFailingRows']), datetime.strptime(scan.get_scan_results()&#91;'dataTimestamp'], '%Y-%m-%dT%H:%M:%S%z')]]
list_of_checks_df = spark.createDataFrame(list_of_checks,schema_checks)
display(list_of_checks_df)</code></pre>



<figure class="wp-block-image size-large is-resized"><img decoding="async" width="1024" height="403" src="https://www.albertnogues.com/wp-content/uploads/2024/05/DataFrameOutput-1024x403.png" alt="" class="wp-image-3441" style="width:840px;height:auto" srcset="https://www.albertnogues.com/wp-content/uploads/2024/05/DataFrameOutput-1024x403.png 1024w, https://www.albertnogues.com/wp-content/uploads/2024/05/DataFrameOutput-300x118.png 300w, https://www.albertnogues.com/wp-content/uploads/2024/05/DataFrameOutput-768x302.png 768w, https://www.albertnogues.com/wp-content/uploads/2024/05/DataFrameOutput-152x60.png 152w, https://www.albertnogues.com/wp-content/uploads/2024/05/DataFrameOutput.png 1328w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>In the case we have the yaml file in our github repo, we can read it and pass it. Or If we are working with Databricks repos and the file is part of out repo we can load it locally</p>



<p>Accessing a remote file and reading it with requests:</p>



<pre class="wp-block-code"><code>#Trying to use a remote yaml file to enforce rules. We can upload it to a github of our own and use it in opur notebook.
#I've created a public repo so i dont need to authenticate to github, but in a real world scenario we should use private repo + secret scopes
customer_quality_rules = 'https://raw.githubusercontent.com/anogues/soda-core-quality-rules/main/soda-core-quality-rules-customer.yaml'
import requests
scan.add_sodacl_yaml_str(requests.get(customer_quality_rules).text)</code></pre>



<p>Or we can load it locally if we are using databricks repos:</p>



<pre class="wp-block-code"><code>scan.add_sodacl_yaml_file("your_file.yaml")</code></pre>
<p>The post <a href="https://www.albertnogues.com/data-quality-checks-with-soda-core-in-databricks/">Data Quality Checks with Soda-Core in Databricks</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Query Delta Tables in the DataLake from PowerBi with Databricks</title>
		<link>https://www.albertnogues.com/query-delta-tables-in-the-datalake-from-powerbi-with-databricks/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=query-delta-tables-in-the-datalake-from-powerbi-with-databricks</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Wed, 15 Nov 2023 18:47:00 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Databricks]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[databricks]]></category>
		<category><![CDATA[powerbi]]></category>
		<category><![CDATA[spark]]></category>
		<category><![CDATA[sql]]></category>
		<guid isPermaLink="false">https://www.albertnogues.com/?p=2195</guid>

					<description><![CDATA[<p>There are several ways to query delta tables from PowerBi. We are going to cover the 4th method here. To do it first we need a service princpal, a secret scope pointing to a databricks keyvault and the password of the SPN stored in this keyvault. Once we have this, the first step is to &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/query-delta-tables-in-the-datalake-from-powerbi-with-databricks/">Query Delta Tables in the DataLake from PowerBi with Databricks</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>There are several ways to query delta tables from PowerBi.</p>



<ul class="wp-block-list">
<li>You can use snowflake with external stages reading delta data from the DL,</li>



<li>The parquet connector and directly query the data from the datalake (caution as this method does not support SPNs and also you can only use delta tables with only 1 version)</li>



<li>or with Delta Sharing plugin (Not possible currently in danone due not having unity catalog)</li>



<li>Or the recommended way, using databricks to do it (With a SPN + Databricks CLuster (either DataEngineering or SQL Warehouse))</li>
</ul>



<p>We are going to cover the 4th method here. To do it first we need a service princpal, a secret scope pointing to a databricks keyvault and the password of the SPN stored in this keyvault.</p>



<p>Once we have this, the first step is to set up the cluster with the credentials to access the datalake. For this we need to configure the spark variables of our databricks cluster. You can follow the guide <a href="https://learn.microsoft.com/en-us/azure/databricks/getting-started/connect-to-azure-storage">here</a>.</p>



<p>After that you cluster should have the credentials in the spark conf section, something like this:</p>



<figure class="wp-block-image size-large"><img decoding="async" width="677" height="1024" src="https://www.albertnogues.com/wp-content/uploads/2023/11/image-1-677x1024.png" alt="" class="wp-image-2197" srcset="https://www.albertnogues.com/wp-content/uploads/2023/11/image-1-677x1024.png 677w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-1-198x300.png 198w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-1-768x1161.png 768w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-1-40x60.png 40w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-1.png 979w" sizes="(max-width: 677px) 100vw, 677px" /></figure>



<p>The second step is creating an EXTERNAL table in Databricks to pint to our delta table(s). For this we connect to our databricks workspace with the previous configuration, and create a new notebook and define the external tables we want, something like this:</p>



<pre class="wp-block-code"><code>%sql
CREATE TABLE IF NOT EXISTS anogues.customers_external 
LOCATION 'abfss://raw@albertdatabricks001.dfs.core.windows.net/customers'</code></pre>



<p>Make sure the location is the right one otherwise when we query the data we will get either an error or no results.</p>



<p>Once we have this we can query our external table and verify we can see the data:</p>



<pre class="wp-block-code"><code>%sql
select * from anogues.customers_external LIMIT 5;</code></pre>



<p>And providing we did it right we should see the data. There is no need to define the table columns as delta uses parquet under the hood so it’s a self contained format where the schema is stored alongside with the data.</p>



<p></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="531" src="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142556-1024x531.png" alt="" class="wp-image-2198" srcset="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142556-1024x531.png 1024w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142556-300x156.png 300w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142556-768x398.png 768w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142556-1536x796.png 1536w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142556-116x60.png 116w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142556.png 1682w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>Once we confirmed this is working we can go to powerbi, try to import data using the Databricks Connector:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="349" src="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142627-1024x349.png" alt="" class="wp-image-2199" srcset="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142627-1024x349.png 1024w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142627-300x102.png 300w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142627-768x262.png 768w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142627-176x60.png 176w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142627.png 1214w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>To configure the connector we need to get some details from our cluster. These can be found in the advanced options of our cluster, in the tab JDBC/ODBC</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="966" src="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142733-1024x966.png" alt="" class="wp-image-2200" srcset="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142733-1024x966.png 1024w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142733-300x283.png 300w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142733-768x726.png 768w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142733-64x60.png 64w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142733.png 1228w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>On the following screen we need to select our authentication options to connect to the databricks cluster. Since we have SAML + SCIM enabled in our workspaces the user and password option is not possible. Either we need a databricks PAT token or use Azure AD. I recommend the latter:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="507" src="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142917-1024x507.png" alt="" class="wp-image-2201" srcset="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142917-1024x507.png 1024w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142917-300x149.png 300w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142917-768x381.png 768w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142917-121x60.png 121w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-142917.png 1235w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>We click on it and select our AAD account. If all works well our session will be started. We should see it in the screen:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="506" src="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-143025-1024x506.png" alt="" class="wp-image-2202" srcset="https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-143025-1024x506.png 1024w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-143025-300x148.png 300w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-143025-768x379.png 768w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-143025-121x60.png 121w, https://www.albertnogues.com/wp-content/uploads/2023/11/image-20230817-143025.png 1223w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>Then we click on connect, and we can see our data. Since we don’t have unity catalog, our table should appear in the hive_metastore catalog. There we can find our database and inside our table(s). We click and either load all the tables we want or start transforming them inside powerbi, like we will do with any other data source.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="867" height="689" src="https://www.albertnogues.com/wp-content/uploads/2023/11/pbi.png" alt="" class="wp-image-2208" srcset="https://www.albertnogues.com/wp-content/uploads/2023/11/pbi.png 867w, https://www.albertnogues.com/wp-content/uploads/2023/11/pbi-300x238.png 300w, https://www.albertnogues.com/wp-content/uploads/2023/11/pbi-768x610.png 768w, https://www.albertnogues.com/wp-content/uploads/2023/11/pbi-76x60.png 76w" sizes="auto, (max-width: 867px) 100vw, 867px" /></figure>



<p>For more help here is the detail of the powerbi databricks connector: <a href="https://learn.microsoft.com/en-us/azure/databricks/partners/bi/power-bi#--connect-power-bi-desktop-to-azure-databricks-manually">Connect Power BI to Azure Databricks &#8211; Azure Databricks | Microsoft Learn</a></p>
<p>The post <a href="https://www.albertnogues.com/query-delta-tables-in-the-datalake-from-powerbi-with-databricks/">Query Delta Tables in the DataLake from PowerBi with Databricks</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Smallest Analytical Platform Ever!</title>
		<link>https://www.albertnogues.com/smallest-analytical-platform-ever/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=smallest-analytical-platform-ever</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Sat, 07 May 2022 08:38:12 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Databricks]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Spark]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[databricks]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[spark]]></category>
		<guid isPermaLink="false">https://www.albertnogues.com/?p=1440</guid>

					<description><![CDATA[<p>I&#8217;ve started working on some of my free time in a project to build the smallest useful analytics platform on the cloud (starting with azure). The purpose is to use it a sa PoC to show to colleagues, managers, prospective customers or just to have fun and play It&#8217;s publicly available on my github repo &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/smallest-analytical-platform-ever/">Smallest Analytical Platform Ever!</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>I&#8217;ve started working on some of my free time in a project to build the smallest useful analytics platform on the cloud (starting with azure).</p>



<p>The purpose is to use it a sa PoC to show to colleagues, managers, prospective customers or just to have fun and play</p>



<p>It&#8217;s publicly available on my github repo and any collaboration is welcome. You can fork it, improve it, send PR&#8217;s and do whatever you want!</p>



<p>The first version will run solely on azure. The objective is to show the following technologies/disciplines:</p>



<p>* Infrastructure as a Code (IaaC), by using Terraform</p>



<p>* Cloud architecture anc Cloud Ops by using an azure cloud environment</p>



<p>* Data Engineering by using a Spark powered Databricks Notebook and an ADF Pipeline (future)</p>



<p>* DevOps to trigger some pipelines based on changes (future)</p>



<p>* Basic Security concepts (keyvault, service principals, least privileged rbac accesses&#8230;)</p>



<p>* FinOps keeping the costs at minimum and choosing the proper tools for the job</p>



<p>* Reporting and Dashboarding on data in the platform</p>



<p>* Data management: We will use an adls storage account and azure sql db</p>



<p>TOOLS:</p>



<p>* Terraform to deploy all the infra as a code</p>



<p>* Azure Cloud to host our resources</p>



<p>You have the code plus all the information on my github repo:</p>



<p><a href="https://github.com/anogues/ProjectZ">https://github.com/anogues/ProjectZ</a></p>



<p></p>
<p>The post <a href="https://www.albertnogues.com/smallest-analytical-platform-ever/">Smallest Analytical Platform Ever!</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Implementing CI/CD in Databricks with Azure DevOps (Part 1)</title>
		<link>https://www.albertnogues.com/implementing-ci-cd-in-databricks-with-azure-devops-part-1/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=implementing-ci-cd-in-databricks-with-azure-devops-part-1</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Sat, 30 Apr 2022 15:10:29 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Databricks]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Spark]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[databricks]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[spark]]></category>
		<guid isPermaLink="false">https://www.albertnogues.com/?p=1416</guid>

					<description><![CDATA[<p>There are many ways to implement some CI/CD with Databricks. We can use Azure DevOps, Github+Github Actions or any other combination of tools, including the dbx tool. But an easy way to just copy notebooks between workspaces can be implemented easily with Azure DevOps. We are going to use the git repos capability of Azure &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/implementing-ci-cd-in-databricks-with-azure-devops-part-1/">Implementing CI/CD in Databricks with Azure DevOps (Part 1)</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>There are many ways to implement some CI/CD with Databricks. We can use Azure DevOps, Github+Github Actions or any other combination of tools, including the <a href="https://dbx.readthedocs.io/en/latest/templates/python_basic.html#project-file-structure" target="_blank" rel="noreferrer noopener">dbx tool</a>.</p>



<p>But an easy way to just copy notebooks between workspaces can be implemented easily with Azure DevOps.</p>



<p>We are going to use the git repos capability of Azure Databricks, so when a new code change is commited in a notebook an Azure DevOps pipeline will trigger the transport copy of the workbook from the first Databricks workspace (in our case a NonProd workspace) to the target one, again, in our case, the Prod workspace.</p>



<p>To achieve this we will use some more components from the Azure ecosystem, including the use of Keyvaults to keep all our secrets stored safely. The list of prerequisites is the following:</p>



<ul class="wp-block-list"><li>Two Databricks workspaces, one our source workspace (NonProd) and another, our Production one.</li><li>An Azure Keyvault (or two if we want to segregate the environments)</li><li>Azure Databricks repository configured at least in our source workspace, so when the change is commited we can triger the pipeline that will fetch the notebook and transport it to the prod workspace</li><li>Access to Azure DevOps (Something similar can be implemented with Github + Github Actions)</li></ul>



<p>Lets see how to implement it. First we need to make sure git repos in enabled our source workspace. We can verify by login with an admin privileged user to our workspace and make sure the option is checked as follows:</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD1-924x1024.png" alt="" class="wp-image-1418" width="687" height="760" srcset="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD1-924x1024.png 924w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD1-271x300.png 271w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD1-768x851.png 768w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD1-54x60.png 54w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD1.png 1072w" sizes="auto, (max-width: 687px) 100vw, 687px" /><figcaption>Fig 1. Make sure that github repos is enabled in our workspace.</figcaption></figure>



<p>Secondly, we go to <a href="https://azure.microsoft.com/en-us/services/devops/" target="_blank" rel="noreferrer noopener">Azure DevOps services</a> and we create a new project. I&#8217;ve called it DatabricksCICD but feel free to call it whatever you need:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="714" src="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD2-1024x714.png" alt="" class="wp-image-1419" srcset="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD2-1024x714.png 1024w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD2-300x209.png 300w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD2-768x536.png 768w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD2-86x60.png 86w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD2.png 1531w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Fig 2. Create a new repo and initialize it</figcaption></figure>



<p>Once created we take the details for cloning our repo and copying them. We go to our databricks workspace and then we look for the Repos option on the left, and add a new repository. We need to paste the url to clone our newle Azure DevOps created repository:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="319" src="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD3-1024x319.png" alt="" class="wp-image-1420" srcset="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD3-1024x319.png 1024w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD3-300x93.png 300w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD3-768x239.png 768w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD3-1536x478.png 1536w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD3-2048x637.png 2048w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD3-193x60.png 193w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Fig 3. Cloning our Azure DevOps Repository</figcaption></figure>



<p>Once we linked our Databricks workspace with our DevOps repo, now we can create a new notebook. In the same Repos section, click on the down arrow to create a new notebook, as shown below:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="889" height="373" src="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD4.png" alt="" class="wp-image-1421" srcset="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD4.png 889w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD4-300x126.png 300w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD4-768x322.png 768w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD4-143x60.png 143w" sizes="auto, (max-width: 889px) 100vw, 889px" /><figcaption>Fig 4.  Creating a new notebook.</figcaption></figure>



<p>The content of the notebook, you can put anything you want. I&#8217;m writing a print(&#8220;Hello from Albert&#8221;) statement. We will not run it, we just want to show it&#8217;s possible to transport it. Once done, click on the save now in the revision tab:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="73" src="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD5-1024x73.png" alt="" class="wp-image-1423" srcset="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD5-1024x73.png 1024w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD5-300x21.png 300w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD5-768x55.png 768w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD5-1536x110.png 1536w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD5-2048x146.png 2048w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD5-600x43.png 600w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Fig 5. Saving our changes to the notebook.</figcaption></figure>



<p>Then click on the left in the main branch button, from there we will be commiting the changes to our repository:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="314" src="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD6-1024x314.png" alt="" class="wp-image-1424" srcset="https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD6-1024x314.png 1024w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD6-300x92.png 300w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD6-768x235.png 768w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD6-1536x470.png 1536w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD6-2048x627.png 2048w, https://www.albertnogues.com/wp-content/uploads/2022/04/DatabricksCICD6-196x60.png 196w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Fig 6. Pushing our notebook to the DevOps repo</figcaption></figure>



<p>If we go back now to our Azure DevOps project we should see the file has been commited to the repository. This ends the first part of this tutorial.</p>



<p>In the second blog entry we will see how to trigger the pipeline after a modification of this notebook and passing the credentials of the second workspace to be able to deliver the changed notebook to our Production (target) workspace.</p>
<p>The post <a href="https://www.albertnogues.com/implementing-ci-cd-in-databricks-with-azure-devops-part-1/">Implementing CI/CD in Databricks with Azure DevOps (Part 1)</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Using Azure Private Endpoints with Databricks</title>
		<link>https://www.albertnogues.com/using-azure-private-endpoints-with-databricks/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=using-azure-private-endpoints-with-databricks</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Thu, 09 Dec 2021 19:31:32 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Databricks]]></category>
		<category><![CDATA[Spark]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[databricks]]></category>
		<category><![CDATA[PrivateEndpoints]]></category>
		<category><![CDATA[spark]]></category>
		<guid isPermaLink="false">https://www.albertnogues.com/?p=1235</guid>

					<description><![CDATA[<p>In this article i will show how to avoing going outside to the internet when using resources inside azure, specially if they are in the same subscription and location (datacenter). Why we may want a private endpoint? Thats a good question. For oth security and performance. Just like using TSCM Equipment for optimal safety and &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/using-azure-private-endpoints-with-databricks/">Using Azure Private Endpoints with Databricks</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this article i will show how to avoing going outside to the internet when using resources inside azure, specially if they are in the same subscription and location (datacenter).</p>



<p>Why we may want a private endpoint? Thats a good question. For oth security and performance. Just like using <a href="https://spyassociates.com/counter-surveillance">TSCM Equipment</a> for optimal safety and security. We dont want the traffic going outside to the internet to return again back to the azure datacenter if the resource we are trying to reach is already there. So with a PrivateLink the traffic will stay inside the Azure backbone network avoiding reaching the internet. More information about private endpoints <a href="https://azure.microsoft.com/en-us/services/private-link/" target="_blank" rel="noreferrer noopener">here</a> and <a href="https://docs.microsoft.com/en-us/azure/private-link/private-link-overview" target="_blank" rel="noreferrer noopener">here</a>.</p>



<p>Though its possible to create private endpoints to connect to services in other subcriptions we will use the same subscription and the West Europe Region in this article. The goal is to connect to both a AzureSQL database using private connectivity and to a datalake using private connectivity as well.</p>



<h2 class="wp-block-heading">Creating a Private Endpoint for AzureSQL and integrating in the databricks vnet</h2>



<p>For this, i created a Databricks workspace and selected to use an already existing VNET, so this way I can add a new subnet for my private endpoints. One of the good things of doing this way is that NICs between subnets see each other and are reacheable (unless we block it with a network security group) but by default traffic is open within the VNET. So I can create a Private endpoint in a specific subnet of the same VNET that hosts the databricks subnets.</p>



<p>Bear in mind that it&#8217;s not possible to add a private endpoint to a subnet managed by databricks. So the two subnets we created, when we deployed the databricks workspace (Bot public and private) should not be modified. We will create a new one as shown in the screen below:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="145" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep4-1024x145.png" alt="" class="wp-image-1236" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep4-1024x145.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep4-300x43.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep4-768x109.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep4-1536x218.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep4-2048x290.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep4-424x60.png 424w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Our  Databricks VNET. Among the two subnets created when the databricks workspace is created i added a new one to host our Private Endpoints</figcaption></figure>



<p>Once defined properly the VNET, we are going to create the private endpoint to reach our AzureSQL through it.</p>



<p>First we need to go to the Azure Portal, find our AzureSQL Server, and click on the left menu called Private Endpoint Connections and click on the plus sing on top to create a new one. We just need to select the subscription, the resource group the nameof the private endpoint and the region. We can fill it as shown in the following picture:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="946" height="626" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep1.png" alt="" class="wp-image-1237" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep1.png 946w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep1-300x199.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep1-768x508.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep1-91x60.png 91w" sizes="auto, (max-width: 946px) 100vw, 946px" /><figcaption class="wp-element-caption">Private Endpoint Creation. Step 1</figcaption></figure>



<p>The second step requires a bit more of information, here we will define which resource we try to target with our private endpoint. As expected we need to find our AzureSQL Server here. We fill the combo boxes as usual</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="920" height="529" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep2.png" alt="" class="wp-image-1238" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep2.png 920w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep2-300x173.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep2-768x442.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep2-330x190.png 330w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep2-104x60.png 104w" sizes="auto, (max-width: 920px) 100vw, 920px" /><figcaption class="wp-element-caption">Private Endpoint Creation. Step 2</figcaption></figure>



<p>The third screen is the most important one. We need to select the VNET and Subnets that will host our private endpoint. In this case we want to use databricks so we need to use the VNET we created for databrickks, and then the subnet we created specifically to host the private endpoints.</p>



<p>Another important step here is to integrate it with the DNS. If we dont integrate it, when we use the AzureSQL hostname provided by azure we will still access through the public endpoint. By integrating it in the DNS. the dns queries over the public endpoint in this private zone, will resolve to the private IP of the NIC of the Private Endpoint</p>



<p>If we chose no for the DNS integration then we will have to add static entries in the /etc/hosts or somewhat, or use the private IP instead of the hostname when connecting to the AzureSQL server. To simplify we choose to integrate it.</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" width="1024" height="583" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep3-1024x583.png" alt="" class="wp-image-1239" style="width:840px;height:478px" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep3-1024x583.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep3-300x171.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep3-768x438.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep3-105x60.png 105w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep3.png 1241w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption"> Private Endpoint Creation. Step 3.</figcaption></figure>



<p>Once created we should see the private endpoint available. If you look at the right its implemented though a NIC (Network Interface card), and by clicking on it, we can find it and see the ip address assigned:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="177" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep5-1024x177.png" alt="" class="wp-image-1241" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep5-1024x177.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep5-300x52.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep5-768x132.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep5-1536x265.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep5-2048x353.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep5-348x60.png 348w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Our newly created Private Endpoint for Azure SQL</figcaption></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="225" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep6-1024x225.png" alt="" class="wp-image-1242" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep6-1024x225.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep6-300x66.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep6-768x168.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep6-1536x337.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep6-2048x449.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep6-274x60.png 274w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Finding the PrivateIP Address of the NIC that implements the private endpoint</figcaption></figure>



<h2 class="wp-block-heading">Test the AzureSQL DB Endpoint from Databricks</h2>



<p>Now we have it ready. We can still see from outside that VNET, that our old server still resolves to a public ip, as it was the case before even inside databricks. We can ping it for testing purposes:</p>



<pre class="wp-block-code"><code>C:\Users\Albert&gt;ping azure-sql-server-albert.database.windows.net

Haciendo ping a cr4.westeurope1-a.control.database.windows.net &#91;<strong>104.40.168.105</strong>] con 32 bytes de datos:</code></pre>



<p>As you can see we have a public ip, but lets try to ping it inside the cluster:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="885" height="178" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep7.png" alt="" class="wp-image-1244" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep7.png 885w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep7-300x60.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep7-768x154.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep7-298x60.png 298w" sizes="auto, (max-width: 885px) 100vw, 885px" /><figcaption class="wp-element-caption">Private endpoint with the DNS integration working fine. Our dns record for the AzureSQL Db does not resolve to a public ip anymore but to the private IP of the PrivateEndpoint</figcaption></figure>



<p>So it&#8217;s working. Its using the private ip instead of the public one. Our last step is to see if we can fetch the data from the database:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="363" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep8-1024x363.png" alt="" class="wp-image-1245" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep8-1024x363.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep8-300x106.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep8-768x272.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep8-1536x544.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep8-2048x726.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep8-169x60.png 169w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Accessing AzureSQL Database though a private endpoint from databricks</figcaption></figure>



<h2 class="wp-block-heading">Creating an AzureDataLake PrivateEndpoint and saving our data to the DataLake through it.</h2>



<p>We are not done yet! We can still complicate matters and create a private endpoint as well to save data to our datalake.</p>



<p>I&#8217;ve created an ADLS Gen 2 storage account, and going back to databricks I see by default it&#8217;s using public access:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="836" height="194" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep9.png" alt="" class="wp-image-1246" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep9.png 836w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep9-300x70.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep9-768x178.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep9-259x60.png 259w" sizes="auto, (max-width: 836px) 100vw, 836px" /><figcaption class="wp-element-caption">Datalake public access</figcaption></figure>



<p>But we can implement a Private Endpoint as well, and route all the traffic through the azure datacenter itself. Lets see how to do it. For achieving this, we go to our ADS Gen2 storage account, and on the left we click again in Networking, and the second tab is called Private Endpoint connections. We click the plus button to create a new one, and basically we follow the same steps as before with a subtle difference</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="930" height="760" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep10.png" alt="" class="wp-image-1247" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep10.png 930w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep10-300x245.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep10-768x628.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep10-73x60.png 73w" sizes="auto, (max-width: 930px) 100vw, 930px" /><figcaption class="wp-element-caption">Creation of a private endpoint for an ADLS Gen2 storage account.</figcaption></figure>



<p>The difference with a storage account is that we need to chose which api we want to create the private endpoint for. We can use the blob, the table, the queue, the file share and the dfs (DataLake) endpoint (And also the static website!).</p>



<p>We will use the dfs endpoint, and again we will place it in the Private Endpoint subnet of our Databricks vnet. Something like this:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="814" height="990" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep11.png" alt="" class="wp-image-1248" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep11.png 814w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep11-247x300.png 247w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep11-768x934.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep11-49x60.png 49w" sizes="auto, (max-width: 814px) 100vw, 814px" /><figcaption class="wp-element-caption">Creating a Private Endpoint for our DataLake an dplacing it in the appropiate subnet</figcaption></figure>



<p>After a few minutes our private endpoint will be ready to be used. We can go again to see the NIC and check the private ip or go directly to databricks and ping the storage account url to see if now it&#8217;s resolving to our private endpoint:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="796" height="184" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep12.png" alt="" class="wp-image-1249" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep12.png 796w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep12-300x69.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep12-768x178.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep12-260x60.png 260w" sizes="auto, (max-width: 796px) 100vw, 796px" /><figcaption class="wp-element-caption">As we can see now databricks resolves our storage account through the private endpoint</figcaption></figure>



<h2 class="wp-block-heading">Test the ADLS Gen2 SA endpoint from Databricks </h2>



<p>If we have the IAM credential Passthrough enabled in our cluster and we have permisison to write to the datalake, now we should be able to write there without going through the internet:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="175" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep13-1024x175.png" alt="" class="wp-image-1250" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep13-1024x175.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep13-300x51.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep13-768x132.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep13-1536x263.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep13-2048x351.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQlPep13-350x60.png 350w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Writing to a DataLake through the Private Endpoint we just created</figcaption></figure>



<p>So this is the end of the tutorial. We created two private endpoints, one for AzureSQL Database and Another for our DataLake and used bot them from Databricks. We also confirmed we are effectively using them by pinging the hostnames of both resources and seeing a change from the public ip to the private one.</p>



<p>Happy Data pojects!</p>
<p>The post <a href="https://www.albertnogues.com/using-azure-private-endpoints-with-databricks/">Using Azure Private Endpoints with Databricks</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Databricks connectivity to Azure SQL / SQL Server</title>
		<link>https://www.albertnogues.com/databricks-connectivity-to-azure-sql-sql-server/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=databricks-connectivity-to-azure-sql-sql-server</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Thu, 09 Dec 2021 10:45:34 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Databricks]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Spark]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://www.albertnogues.com/?p=1224</guid>

					<description><![CDATA[<p>Most of the developments I see inside databricks rely on fetching or writing data to some sort of Database. Usually the preferred method for this is though the use of jdbc driver, as most databases offer some sort of jdbc driver. In some cases, though, its also possible to use some spark optimized driver. This &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/databricks-connectivity-to-azure-sql-sql-server/">Databricks connectivity to Azure SQL / SQL Server</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Most of the developments I see inside databricks rely on fetching or writing data to some sort of Database.</p>



<p>Usually the preferred method for this is though the use of jdbc driver, as most databases offer some sort of jdbc driver.</p>



<p>In some cases, though, its also possible to use some spark optimized driver. This is the case in Azure SQL / SQL Server. We have still the option to use the standard jdbc driver (what most people do because it&#8217;s standard to all databases) but we can improve the performance by using a specific spark driver. Till some time ago it was only supported with the Scala API but now it&#8217;s possible to be used in Python and R as well, so there is no reason not to give it a try.</p>



<p>In this article we will see the two options to make this connectivity. For the test purposes we will connect to an Azure SQL in the same region (West Europe).</p>



<h2 class="wp-block-heading">Connecting to AzureSQL through  jdbc driver. </h2>



<p>In this case the jdbc driver is already shipped in the databricks cluster, we do not need to install anything. We just can connect directly. Lets see how (We have a scala example <a href="https://docs.microsoft.com/es-es/azure/databricks/data/data-sources/sql-databases" target="_blank" rel="noreferrer noopener">here</a> but i will use python for this example)</p>



<pre class="wp-block-code"><code>#In a real development this should be fetched from a keyvault using a secret scope with: dbutils.secrets.get(scope = "sql_db", key = "username") and  dbutils.secrets.get(scope = "sql_db", key = "password")

jdbcDF = spark.read.format("jdbc") \
    .option("url", f"jdbc:sqlserver://azure-sql-server-albert.database.windows.net:1433;databaseName=databricksdata") \
    .option("dbtable", "SalesLT.Product") \
    .option("user", "anogues") \
    .option("password", "XXXXXX") \
    .option("driver", "com.microsoft.sqlserver.jdbc.SQLServerDriver") \
    .load()

jdbcDF.show()</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="362" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR1-1024x362.png" alt="" class="wp-image-1226" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR1-1024x362.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR1-300x106.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR1-768x272.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR1-1536x543.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR1-2048x724.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR1-170x60.png 170w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Spark Dataframe from a JDBC Azure SQL DB Source</figcaption></figure>



<p>So as we saw we have been able to connect successfully to our Azure SQL DB using the jdbc driver shipped with databricks. Lets now try to change to the spark optimized driver</p>



<h2 class="wp-block-heading">Connecting to AzureSQL through the spark optimized driver</h2>



<p>To connect using the spark optimized driver, first we need to install the driver in the cluster, as it&#8217;s not available by default.</p>



<p>The driver is available in Maven for both spark 2.X and 3.X. In the microsoft <a href="https://docs.microsoft.com/en-us/sql/connect/spark/connector?view=sql-server-ver15">website</a> we can find more information on where to get them and how to use them. For this exercise purposes we will inbstall it through databricks libraries, using maven. Just add in the coordinates box the following: com.microsoft.azure:spark-mssql-connector_2.12:1.2.0 as can be seen in the image below</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="323" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR2-1024x323.png" alt="" class="wp-image-1227" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR2-1024x323.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR2-300x95.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR2-768x242.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR2-1536x485.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR2-190x60.png 190w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR2.png 2006w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Installing the spark AzureSQL Driver from Maven</figcaption></figure>



<p>Once installed we should see a green dot next to the driver, and this will mean the driver is ready to be used. We go back to our notebook and try</p>



<pre class="wp-block-code"><code>#In a real development this should be fetched from a keyvault using a secret scope with: dbutils.secrets.get(scope = "sql_db", key = "username") and  dbutils.secrets.get(scope = "sql_db", key = "password")
jdbcDF = spark.read.format("com.microsoft.sqlserver.jdbc.spark") \
    .option("url", f"jdbc:sqlserver://azure-sql-server-albert.database.windows.net:1433;databaseName=databricksdata") \
    .option("dbtable", "SalesLT.Product") \
    .option("user", "anogues") \
    .option("password", "XXXXXX") \
    .load()

jdbcDF.show()</code></pre>



<p>If we see an error like java.lang.ClassNotFoundException: com.microsoft.sqlserver.jdbc.spark this means that the driver can&#8217;t be found, so probably it&#8217;s not properly installed. Check back the libraries in the cluster and make sure the status is installed. If all goes well we should see again our dataframe:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="351" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR3-1-1024x351.png" alt="" class="wp-image-1229" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR3-1-1024x351.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR3-1-300x103.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR3-1-768x263.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR3-1-1536x526.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR3-1-2048x701.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR3-1-175x60.png 175w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption> Spark Dataframe from a Spark Azure SQL DB Source </figcaption></figure>



<p>The reason why we should use the optimized spark driver is usually because of performance reasons. Microsoft claims its about 15x faster than the jdbc one. But there is more. The spark driver also allows AAD authentication either by using a service principal or an AAD account, apart of course from the native sql server authentication. Lets try if it works with an AAD account:</p>



<pre class="wp-block-code"><code>jdbcDF = spark.read \
    .format("com.microsoft.sqlserver.jdbc.spark") \
    .option("url", f"jdbc:sqlserver://azure-sql-server-albert.database.windows.net:1433;databaseName=databricksdata") \
    .option("dbtable", "SalesLT.Product") \
    .option("authentication", "ActiveDirectoryPassword") \
    .option("user", "sqluser@anogues4hotmail.onmicrosoft.com") \
    .option("password", "XXXXXX") \
    .option("encrypt", "true") \
    .option("hostNameInCertificate", "*.database.windows.net") \
    .load()
jdbcDF.show()</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="366" src="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR4-1024x366.png" alt="" class="wp-image-1230" srcset="https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR4-1024x366.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR4-300x107.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR4-768x274.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR4-1536x549.png 1536w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR4-2048x732.png 2048w, https://www.albertnogues.com/wp-content/uploads/2021/12/SQLDBR4-168x60.png 168w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>For using a service principal you need to generate a token. In python this can be accomplished with the <a href="https://pypi.org/project/adal/" target="_blank" rel="noreferrer noopener">adal</a> library (That needs to be installed in the cluster as well from pypi). You have a sample notebook in microsoft spark driver github account <a href="https://github.com/microsoft/sql-spark-connector/tree/master/samples/Databricks-AzureSQL/DatabricksNotebooks">here</a>.</p>



<p>More information about the driver can be found on the microsoft github repository <a href="https://github.com/microsoft/sql-spark-connector" target="_blank" rel="noreferrer noopener">here</a>.</p>
<p>The post <a href="https://www.albertnogues.com/databricks-connectivity-to-azure-sql-sql-server/">Databricks connectivity to Azure SQL / SQL Server</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Ansible playbook to configure Azure Red Hat VM&#8217;s</title>
		<link>https://www.albertnogues.com/ansible-playbook-to-configure-azure-red-hat-vms/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=ansible-playbook-to-configure-azure-red-hat-vms</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Wed, 21 Apr 2021 10:43:36 +0000</pubDate>
				<category><![CDATA[Ansible]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[ansible]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[lun]]></category>
		<category><![CDATA[machine]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[redhat]]></category>
		<category><![CDATA[ulimit]]></category>
		<category><![CDATA[vm]]></category>
		<guid isPermaLink="false">https://www.albertnogues.com/?p=1067</guid>

					<description><![CDATA[<p>In todays post I am going to share an ansible playbook to configure a new VM recently launched. This playbook contains the following: Change Admin password Create linux Group Add user to several groups Create a new user with specific salted password (Check point 3 for generating the hashed salt) Find all the mounted disks &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/ansible-playbook-to-configure-azure-red-hat-vms/">Ansible playbook to configure Azure Red Hat VM&#8217;s</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In todays post I am going to share an ansible playbook to configure a new VM recently launched. This playbook contains the following:</p>



<ul class="wp-block-list"><li>Change Admin password</li><li>Create linux Group</li><li>Add user to several groups</li><li>Create a new user with specific salted password (Check point 3 for generating the hashed salt)</li><li>Find all the mounted disks in any LUN and format them (create a xfs filesystem)</li><li>Create a mountpoint (in /opt)</li><li>Mount the disk formatted in LUN 0 PART 1 in the specified mountoint in fstab, so it will be persistent across reboots</li><li>Register the OS into RHEL Satellite so yum is usable</li><li>Disable some local repo in an external /mnt drive that crashes yum in the image</li><li>Install Telnet and other packages</li><li>Modify some Ulimits</li><li>Register Machine in a domain (TO BE DONE)</li></ul>



<p>To Start, lets create first the ansible inventory file and call it inventory.yaml (Or use init format if you want). The format of the file should be something like the folowing:</p>



<pre class="wp-block-code"><code>all:
  children:
    myservers:
      hosts:
        SERVER001:
          ansible_host: 10.10.1.1
        SERVER002:
          ansible_host: 10.10.1.2
        SERVER003:
          ansible_host: 10.10.1.3
        SERVER004:
          ansible_host: 10.10.1.4
        SERVER005:
          ansible_host: 10.10.1.5
    alberttest:
      hosts:
        testalbert001:
          ansible_host: 10.10.1.6</code></pre>



<p>Then we can create our playbook file (I will comment it below). This will be another yaml file with the following content:</p>



<pre class="wp-block-code"><code>---
- hosts: myservers
  collections:
    - ansible.posix
  tasks:
  - name: Ping the Server
    ping:

  - name: Change Password of the Admin user.
    user:
      name: Admin
      # python3 -c 'import crypt; print (crypt.crypt("Passw0rd", "$1$SomeSalt$"))'
      password: $1$SomeSalt$C7s11A7tyf8OKOg0JoCYp/

  - name: Create anogues group
    group:
      name: anogues
      state: present

  - name: Create anogues user
    user:
      name: anogues
      password: $1$SomeSalt$C7s11A7tyf8OKOg0JoCYp/
      shell: /bin/bash
      groups: infaedc, wheel
      append: yes

  - name: Create mountpoint
    file: path=/opt/data state=directory

  - name: Find all Luns
    find:
      paths: /dev/disk/azure/scsi1/
      file_type: link
      recurse: No
      patterns: "lun?"
    register: files_matched

  - name: Partition Disk to Max
    shell: "parted {{ item.path }} --script mklabel gpt mkpart xfspart xfs 0% 100%"
    args:
      executable: /bin/bash
    loop: "{{ files_matched.files|flatten(levels=1) }}"

  - name: Inform OS of partition table changes
    command: partprobe

  - name: find UUID of sdX1
    shell: |
      blkid -s UUID -o value $(readlink -f /dev/disk/azure/scsi1/lun0-part1)
    register: uuid

  - name: show real uuid
    debug:
      msg: "{{ uuid.stdout }}"

  - name: Mount disk drive in fstab
    mount:
      path: /opt/data
      src: 'UUID={{uuid.stdout}}'
      fstype: xfs
      opts: defaults,nofail
      dump: 1
      passno: 2
      state: mounted

  - name: Check disk Status
    shell: df -h | grep /dev/sd
    register: df2_status

  - name: Show mounted FS
    debug:
      msg: "{{ df2_status.stdout_lines }}"

  - name: Register RHEL
    redhat_subscription:
      state: present
      username: {{ lookup('env', 'RHEL_USER') }}
      password: {{ lookup('env', 'RHEL_PASSWORD') }}
      autosubscribe: yes

  - name: Disable Media Repo
    ini_file:
      dest: /etc/yum.repos.d/media.repo
      section: "{{item}}"
      option: enabled
      value: 0
    with_items:
      - LocalRepo_BaseOS
      - LocalRepo_AppStream

  - name: Install Telnet and other packags
    yum:
      name:
        - telnet
        - curl
        - zip
        - unzip
        - tar
        - wget
        - libcurl
      state: present

  - name: Add or modify nproc hard limit for the user anogues. Set 65k value.
    pam_limits:
      domain: anogues
      limit_type: hard
      limit_item: nproc
      value: 65000
    become: yes
    become_method: sudo
    become_user: root

  - name: Add or modify nproc soft limit for the user anogues. Set 65k value.
    pam_limits:
      domain: anogues
      limit_type: soft
      limit_item: nproc
      value: 65000
    become: yes
    become_method: sudo
    become_user: root</code></pre>



<p>Let&#8217;s have a look at how it works:</p>



<p>One of the playbook steps is to register RHEL into Satellite. To avoid hardcoding the user and password in the playbooy these values will be taken from your env vars, so please export them before</p>



<p>export RHEL_USER=<br>export RHEL_PASSWORD=</p>



<p>You need to install ansible-posix first (Tested with 1.2.0.). Download it from here: https://galaxy.ansible.com/ansible/posix<br>ansible-galaxy install ansible-posix-1.2.0.tar.gz</p>



<p>To create a crypted user with a salt you can do the following<br>python3 -c &#8216;import crypt; print (crypt.crypt(&#8220;YOUR_UNHASHED_PASSWORD&#8221;, &#8220;$1$SomeSalt$&#8221;))&#8217;<br>And copy the hashed salt into the playbook area for the user you want to set the password</p>



<p>Azure Mounts the Disk Drives by Luns. By default it starts in Lun0, So i am using the softlink to /dev/disk/azure to check the disks added. This playbook will only automount the first disk. If you need more simply modify the playbook and add as many luns as you need</p>



<p># tree /dev/disk/azure<br>/dev/disk/azure<br>├── resource -&gt; ../../sdb<br>├── resource-part1 -&gt; ../../sdb1<br>├── root -&gt; ../../sda<br>├── root-part1 -&gt; ../../sda1<br>├── root-part14 -&gt; ../../sda14<br>├── root-part15 -&gt; ../../sda15<br>├── root-part2 -&gt; ../../sda2<br>└── scsi1<br>├── lun0 -&gt; ../../../sdc<br>└── lun1 -&gt; ../../../sdd</p>



<p>Here there are two disks mounted in two luns, 0 and 1. No disk has been formatted yet as we only see sdc and sdd drives with no partition. Once the first one is formatted, you will see a difference:</p>



<p>/dev/disk/azure<br>├── resource -&gt; ../../sdb<br>├── resource-part1 -&gt; ../../sdb1<br>├── root -&gt; ../../sda<br>├── root-part1 -&gt; ../../sda1<br>├── root-part14 -&gt; ../../sda14<br>├── root-part15 -&gt; ../../sda15<br>├── root-part2 -&gt; ../../sda2<br>└── scsi1<br>├── lun0 -&gt; ../../../sdc<br>├── lun0-part1 -&gt; ../../../sdc1<br>└── lun1 -&gt; ../../../sdd</p>



<p>To run it (make sure you have installed ansible 2.9 from RHEL repo):</p>



<pre class="wp-block-code"><code>ansible-playbook -i inventory.yaml -k playbook.yaml</code></pre>



<p>The playbook also formats all drives found in any luns (All disks mounted in the VM), bout only mounts the first one as we only defined one mount point.</p>



<p>If we need to modify it, we can include all the tasks that ar enecessary (read the blkid, add entry in fstab into another loop block but previously we have to create as many directories for the mountpoints as necessay and feed them in the loop block, so they are assigned concurrently.</p>



<p>Links of interest:</p>



<p><a href="https://stackoverflow.com/questions/19292899/creating-a-new-user-and-password-with-ansible">https://stackoverflow.com/questions/19292899/creating-a-new-user-and-password-with-ansible</a></p>



<p><a href="https://stackoverflow.com/questions/49424967/how-to-create-azure-vm-with-data-disk-and-then-format-it-via-ansible">https://stackoverflow.com/questions/49424967/how-to-create-azure-vm-with-data-disk-and-then-format-it-via-ansible</a></p>
<p>The post <a href="https://www.albertnogues.com/ansible-playbook-to-configure-azure-red-hat-vms/">Ansible playbook to configure Azure Red Hat VM&#8217;s</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Load data from azure blob storage and run TPC-DS queries on Azure Synapse.</title>
		<link>https://www.albertnogues.com/load-data-from-azure-blob-storage-and-run-tpc-ds-queries-on-azure-synapse/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=load-data-from-azure-blob-storage-and-run-tpc-ds-queries-on-azure-synapse</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Wed, 03 Feb 2021 15:35:20 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[BigData]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">http://www.albertnogues.com.preview.services/?p=1023</guid>

					<description><![CDATA[<p>In this article we will see how to provision an azure synapse cluster, load some large quantity of data from azure blob storage and run a query to see the contents and check performance. I plan to write a serie of articles arround data warehousing in the cloud so check out for new articles soon. &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/load-data-from-azure-blob-storage-and-run-tpc-ds-queries-on-azure-synapse/">Load data from azure blob storage and run TPC-DS queries on Azure Synapse.</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this article we will see how to provision an azure synapse cluster, load some large quantity of data from azure blob storage and run a query to see the contents and check performance. I plan to write a serie of articles arround data warehousing in the cloud so check out for new articles soon.</p>



<p>I’ve split the article in 3 steps that cover diverse topics:</p>



<ul class="wp-block-list"><li>Part 1. Deploying a synapse cluster.</li><li>Part 2. Load TPC-DS data.</li><li>Part 3. Run queries to verify the performance.</li></ul>



<ol class="wp-block-list"><li>We need to create a synapse cluster. We head to the azure portal and we will create a Gen2: DW100c cluster, which is the cheapest option on sale for a bit more than 1.50 USD per hour. For this exercise I didnt create a synapse workspace, i just went with the &#8220;Dedicated SQL pool&#8221; because i am only interested in the synapse db warehouse, not spark or other engines this time. Check <a href="https://docs.microsoft.com/en-us/azure/synapse-analytics/sql-data-warehouse/sql-data-warehouse-overview-what-is" target="_blank" rel="noreferrer noopener">the following</a> documentation for more help. </li></ol>



<p>After a few minutes we will have the synapse warehouse ready.</p>



<p>Once created, we can take the url and connect from Dbeaver (or any other editor, you can use the free <a href="https://docs.microsoft.com/en-us/sql/azure-data-studio/download-azure-data-studio?view=sql-server-ver15" data-type="URL" data-id="https://docs.microsoft.com/en-us/sql/azure-data-studio/download-azure-data-studio?view=sql-server-ver15" target="_blank" rel="noreferrer noopener">Azure Data Studio too!</a>) to see if all is ok:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="555" height="625" src="https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse1.png" alt="" class="wp-image-1052" srcset="https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse1.png 555w, https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse1-266x300.png 266w, https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse1-53x60.png 53w" sizes="auto, (max-width: 555px) 100vw, 555px" /><figcaption>Check connectivity with the newly created synapse instance</figcaption></figure>



<p>If you have trouble, make sure you have your ip added in the whitelist of the firewall section of your synapse instance and that is open to the public:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="638" height="498" src="https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse2.png" alt="" class="wp-image-1053" srcset="https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse2.png 638w, https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse2-300x234.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse2-77x60.png 77w" sizes="auto, (max-width: 638px) 100vw, 638px" /><figcaption>Adding an IP to our synapse instance</figcaption></figure>



<p>2. Load TPC-DS Data</p>



<p>Unfortunatelly I haven&#8217;t been able to found any TPC-DS data in azure blob storage. The table structure I am going to use, is the fivetran table structure for azure synapse found in their repo <a rel="noreferrer noopener" href="https://github.com/fivetran/benchmark/blob/master/500-PopulateAzure.sql" data-type="URL" data-id="https://github.com/fivetran/benchmark/blob/master/500-PopulateAzure.sql" target="_blank">here</a>, albeit i will do a modification stated in <a rel="noreferrer noopener" href="https://www.atscale.com/wp-content/uploads/2020/07/AtScale-Cloud-Data-Warehouse-Benchmark-Azure-Synapse-Analytics-SQL-20200813-1.pdf" data-type="URL" data-id="https://www.atscale.com/wp-content/uploads/2020/07/AtScale-Cloud-Data-Warehouse-Benchmark-Azure-Synapse-Analytics-SQL-20200813-1.pdf" target="_blank">this atscale pdf</a> as I think it makes more sense. Basically, for the dimension tables we will replicate them and create a clustered columnstore index, and for the fact table, we will use a hash distribution by the sr_item_sk column. They also suggest a columnstore index order in the fact tables but for our test i dont think it&#8217;s necessary.</p>



<p>The table structure, extracted from fivetran repo that we are going to replicate is the same set of 4 tables i did for my previous article for redshift, that you can find <a href="http://www.albertnogues.com.preview.services/load-data-from-s3-and-run-tpc-ds-queries-on-amazon-redshift/" data-type="URL" data-id="http://www.albertnogues.com.preview.services/load-data-from-s3-and-run-tpc-ds-queries-on-amazon-redshift/">here</a>. Here is the modified list:</p>



<pre class="wp-block-code"><code>create table customer_address (
    ca_address_sk             bigint,
    ca_address_id             nvarchar(16),
    ca_street_number          nvarchar(10),
    ca_street_name            nvarchar(60),
    ca_street_type            nvarchar(15),
    ca_suite_number           nvarchar(10),
    ca_city                   nvarchar(60),
    ca_county                 nvarchar(30),
    ca_state                  nvarchar(2),
    ca_zip                    nvarchar(10),
    ca_country                nvarchar(20),
    ca_gmt_offset             float,
    ca_location_type          nvarchar(20)
)
WITH
( 
  DISTRIBUTION = REPLICATE,
  CLUSTERED COLUMNSTORE INDEX
)
GO

create table customer (
    c_customer_sk             bigint,
    c_customer_id             nvarchar(16),
    c_current_cdemo_sk        bigint,
    c_current_hdemo_sk        bigint,
    c_current_addr_sk         bigint,
    c_first_shipto_date_sk    bigint,
    c_first_sales_date_sk     bigint,
    c_salutation              nvarchar(10),
    c_first_name              nvarchar(20),
    c_last_name               nvarchar(30),
    c_preferred_cust_flag     nvarchar(1),
    c_birth_day               int,
    c_birth_month             int,
    c_birth_year              int,
    c_birth_country           nvarchar(20),
    c_login                   nvarchar(13),
    c_email_address           nvarchar(50),
    c_last_review_date        nvarchar(10)
)
WITH
( 
  DISTRIBUTION = REPLICATE,
  CLUSTERED COLUMNSTORE INDEX
)
GO

create table date_dim (
    d_date_sk                 bigint,
    d_date_id                 nvarchar(16),
    d_date                    nvarchar(10),
    d_month_seq               int,
    d_week_seq                int,
    d_quarter_seq             int,
    d_year                    int,
    d_dow                     int,
    d_moy                     int,
    d_dom                     int,
    d_qoy                     int,
    d_fy_year                 int,
    d_fy_quarter_seq          int,
    d_fy_week_seq             int,
    d_day_name                nvarchar(9),
    d_quarter_name            nvarchar(6),
    d_holiday                 nvarchar(1),
    d_weekend                 nvarchar(1),
    d_following_holiday       nvarchar(1),
    d_first_dom               int,
    d_last_dom                int,
    d_same_day_ly             int,
    d_same_day_lq             int,
    d_current_day             nvarchar(1),
    d_current_week            nvarchar(1),
    d_current_month           nvarchar(1),
    d_current_quarter         nvarchar(1),
    d_current_year            nvarchar(1) 
)
WITH
( 
  DISTRIBUTION = REPLICATE,
  CLUSTERED COLUMNSTORE INDEX
)
GO

create table web_returns (
    wr_returned_date_sk       bigint,
    wr_returned_time_sk       bigint,
    wr_item_sk                bigint,
    wr_refunded_customer_sk   bigint,
    wr_refunded_cdemo_sk      bigint,
    wr_refunded_hdemo_sk      bigint,
    wr_refunded_addr_sk       bigint,
    wr_returning_customer_sk  bigint,
    wr_returning_cdemo_sk     bigint,
    wr_returning_hdemo_sk     bigint,
    wr_returning_addr_sk      bigint,
    wr_web_page_sk            bigint,
    wr_reason_sk              bigint,
    wr_order_number           bigint,
    wr_return_quantity        int,
    wr_return_amt             float,
    wr_return_tax             float,
    wr_return_amt_inc_tax     float,
    wr_fee                    float,
    wr_return_ship_cost       float,
    wr_refunded_cash          float,
    wr_reversed_charge        float,
    wr_account_credit         float,
    wr_net_loss               float
)
WITH
( 
  DISTRIBUTION = HASH(wr_item_sk),
  CLUSTERED COLUMNSTORE INDEX
)
GO</code></pre>



<pre class="wp-block-code"><code>19:33:36Started executing query at Line 2
Commands completed successfully.
19:33:36Started executing query at Line 23
Commands completed successfully.
19:33:36Started executing query at Line 50
Commands completed successfully.
19:33:36Started executing query at Line 87
Commands completed successfully.
Total execution time: 00:00:25.515</code></pre>



<p>So we are good for the table structure. Its now time to import the data. We can try to copy it from an azure blob storage where fivetran has left the data already generated. I tried with their suggested COPY INTO COMMAND but didnt work for me because the row terminator was not specified, so if you have problems use the following statements that worked for me:</p>



<pre class="wp-block-code"><code>copy into date_dim
from 'https://fivetranbenchmark.blob.core.windows.net/tpcds/tpcds_1000_dat/date_dim/'
with (file_type = 'CSV', fieldterminator = '|', ENCODING = 'UTF8', ROWTERMINATOR='0X0A');


copy into customer
from 'https://fivetranbenchmark.blob.core.windows.net/tpcds/tpcds_1000_dat/customer/'
with (file_type = 'CSV', fieldterminator = '|', ENCODING = 'UTF8', ROWTERMINATOR='0X0A');

copy into customer_address
from 'https://fivetranbenchmark.blob.core.windows.net/tpcds/tpcds_1000_dat/customer_address/'
with (file_type = 'CSV', fieldterminator = '|', ENCODING = 'UTF8', ROWTERMINATOR='0X0A');


copy into web_returns
from 'https://fivetranbenchmark.blob.core.windows.net/tpcds/tpcds_1000_dat/web_returns/'
with (file_type = 'CSV', fieldterminator = '|', ENCODING = 'UTF8', ROWTERMINATOR='0X0A');</code></pre>



<p>And after a few minutes (the fact table is a bit more than 10GB big) you will have your data loaded:</p>



<pre class="wp-block-code"><code>20:07:41Started executing query at Line 125
(73049 rows affected)
(12000000 rows affected)
(6000000 rows affected)
(71997522 rows affected)
Total execution time: 00:06:53.032</code></pre>



<p>Note that we are using the 1TB set for Azure Synapse vs the 3 TB test we used for redshift so the comparison wouln&#8217;t be fair with this dataset. You can see the difference as our fact table has approximately 1/3 of the rows than in the <a rel="noreferrer noopener" href="http://www.albertnogues.com.preview.services/load-data-from-s3-and-run-tpc-ds-queries-on-amazon-redshift/" data-type="URL" data-id="http://www.albertnogues.com.preview.services/load-data-from-s3-and-run-tpc-ds-queries-on-amazon-redshift/" target="_blank">redshift test</a>. But still with that, we have a table with almost 72 million rows. If you want to generate another bigger set of data you can do it by using the <a rel="noreferrer noopener" href="https://github.com/fivetran/benchmark/blob/master/002-GenerateData.sh" data-type="URL" data-id="https://github.com/fivetran/benchmark/blob/master/002-GenerateData.sh" target="_blank">generate_data.sh</a> provided by fivetran and load the data to your own azure blob storage container.</p>



<p>3. Start running the queries.</p>



<p>As I did for the previous test with redshift, I will use query30 to test this. The query can be found in <a rel="noreferrer noopener" href="https://github.com/fivetran/benchmark/blob/master/microsoft_sql/query30.sql" data-type="URL" data-id="https://github.com/fivetran/benchmark/blob/master/microsoft_sql/query30.sql" target="_blank">fivetran github repo</a> updated to run with synapse, but you can adapt the original query as you want.</p>



<pre class="wp-block-code"><code>WITH customer_total_return 
     AS (SELECT wr_returning_customer_sk AS ctr_customer_sk, 
                ca_state                 AS ctr_state, 
                Sum(wr_return_amt)       AS ctr_total_return 
         FROM   web_returns, 
                date_dim, 
                customer_address 
         WHERE  wr_returned_date_sk = d_date_sk 
                AND d_year = 2000 
                AND wr_returning_addr_sk = ca_address_sk 
         GROUP  BY wr_returning_customer_sk, 
                   ca_state) 
SELECT TOP 100 c_customer_id, 
               c_salutation, 
               c_first_name, 
               c_last_name, 
               c_preferred_cust_flag, 
               c_birth_day, 
               c_birth_month, 
               c_birth_year, 
               c_birth_country, 
               c_login, 
               c_email_address, 
               c_last_review_date, 
               ctr_total_return 
FROM   customer_total_return ctr1, 
       customer_address, 
       customer 
WHERE  ctr1.ctr_total_return &gt; (SELECT Avg(ctr_total_return) * 1.2 
                                FROM   customer_total_return ctr2 
                                WHERE  ctr1.ctr_state = ctr2.ctr_state) 
       AND ca_address_sk = c_current_addr_sk 
       AND ca_state = 'IN' 
       AND ctr1.ctr_customer_sk = c_customer_sk 
ORDER  BY c_customer_id, 
          c_salutation, 
          c_first_name, 
          c_last_name, 
          c_preferred_cust_flag, 
          c_birth_day, 
          c_birth_month, 
          c_birth_year, 
          c_birth_country, 
          c_login, 
          c_email_address, 
          c_last_review_date, 
          ctr_total_return</code></pre>



<p>And the result:</p>



<pre class="wp-block-code"><code>20:31:22Started executing query at Line 145
(100 rows affected)
Total execution time: 00:01:40.416</code></pre>



<p>And a pic with the 100 first rows:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="269" src="https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse3-1024x269-1.png" alt="" class="wp-image-1054" srcset="https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse3-1024x269-1.png 1024w, https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse3-1024x269-1-300x79.png 300w, https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse3-1024x269-1-768x202.png 768w, https://www.albertnogues.com/wp-content/uploads/2021/02/Synapse3-1024x269-1-228x60.png 228w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>
<p>The post <a href="https://www.albertnogues.com/load-data-from-azure-blob-storage-and-run-tpc-ds-queries-on-azure-synapse/">Load data from azure blob storage and run TPC-DS queries on Azure Synapse.</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Deploying an Azure Storage Account with a queue with terraform and python</title>
		<link>https://www.albertnogues.com/deploying-and-interacting-with-an-azure-storage-account-and-a-queue-with-terraform-and-python-part-i/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=deploying-and-interacting-with-an-azure-storage-account-and-a-queue-with-terraform-and-python-part-i</link>
		
		<dc:creator><![CDATA[Albert]]></dc:creator>
		<pubDate>Wed, 23 Dec 2020 14:39:14 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Queue]]></category>
		<category><![CDATA[Storage Account]]></category>
		<category><![CDATA[Terraform]]></category>
		<guid isPermaLink="false">http://192.168.1.40/?p=939</guid>

					<description><![CDATA[<p>In this post we will deploy the azure infrastructure to have an storage account queue. For this we will use terraform which can be downloaded from here. Before start, we need a few prerequisites, these are the following: Installing terraform, azure-cli and azure-storage-queue Create a service principal for deploying the resources with terraform Creating the &#8230; </p>
<p>The post <a href="https://www.albertnogues.com/deploying-and-interacting-with-an-azure-storage-account-and-a-queue-with-terraform-and-python-part-i/">Deploying an Azure Storage Account with a queue with terraform and python</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this post we will deploy the azure infrastructure to have an storage account queue. For this we will use terraform which can be downloaded from <a rel="noreferrer noopener" href="https://www.terraform.io/downloads.html" target="_blank">here</a>.</p>



<p>Before start, we need a few prerequisites, these are the following:</p>



<ol class="wp-block-list"><li>Installing terraform, azure-cli and azure-storage-queue</li><li>Create a service principal for deploying the resources with terraform</li><li>Creating the terraform tf file with all the components required to be deployed. These include:<ul><li>A Resource Group</li><li>A Storage account</li><li>A queue inside the previous storage account</li></ul></li><li>Run the terraform deployment</li><li>Write the python code to send a message and retrieve it from the queue created in the storage account (Part II)</li></ol>



<p>Let&#8217;s start!</p>



<p>Before starting to work with terraform, we need to install terraform. We can do it easily downloading it from the official website and placing it in the path of our sysvars. We can download the latest packaged binary from <a rel="noreferrer noopener" href="https://www.terraform.io/downloads.html" target="_blank">here</a>.</p>



<p>To install azure-cli and azure-storage-queue we can use python + pip. If you dont have python, you can download it from the official website <a rel="noreferrer noopener" href="https://www.python.org/downloads/" target="_blank">here</a>. Make suire you install pip as well as part of the installation process.</p>



<p>Once python is setup we can run the following to install the packages and it&#8217;s dependencies:</p>



<pre class="wp-block-code"><code>pip install azure-cli azure-queue-storage</code></pre>



<p>After installing the packages we are good to start moving to point 2. For the service principal creation we can use the recently installed azure-cli. The call to create the spn is as follows:</p>



<pre class="wp-block-code"><code>az ad sp create-for-rbac --name="<strong><span class="has-inline-color has-vivid-red-color">SPForTerraform</span></strong>" --role="Contributor" --scopes="/subscriptions/<strong><span class="has-inline-color has-vivid-red-color">ourSubscriptionId</span></strong>"</code></pre>



<p>Make sure you replace ourSubscriptionId by your azure subscription. In case you dont know where to obtain it you can follow the following <a rel="noreferrer noopener" href="https://docs.microsoft.com/en-us/azure/media-services/latest/how-to-set-azure-subscription?tabs=portal" data-type="URL" data-id="https://docs.microsoft.com/en-us/azure/media-services/latest/how-to-set-azure-subscription?tabs=portal" target="_blank">procedure</a> or <a href="https://docs.bitnami.com/azure/faq/administration/find-subscription-id/" data-type="URL" data-id="https://docs.bitnami.com/azure/faq/administration/find-subscription-id/" target="_blank" rel="noreferrer noopener">this one</a>. The spn we will create will be named with the &#8211;name parameter and will have the contributor role. This means this spn will have access to everything but to manage users. So we can create any sort of resouce with in. This process may take a few seconds, after a while we should see something like this:</p>



<pre class="wp-block-code"><code>Changing "SPForTerraform" to a valid URI of "http://SPForTerraform", which is the required format used for service principal names
Creating a role assignment under the scope of "/subscriptions/xxxxxx"
  Retrying role assignment creation: 1/36
  Retrying role assignment creation: 2/36
  Retrying role assignment creation: 3/36
The output includes credentials that you must protect. Be sure that you do not include these credentials in your code or check the credentials into your source control. For more information, see https://aka.ms/azadsp-cli
{
  "appId": "xxxxxx",
  "displayName": "SPForTerraform",
  "name": "http://SPForTerraform",
  "password": "yyyyyy",
  "tenant": "zzzzzz"
}</code></pre>



<p>Make sure you protect these, as anybody getting them will be able to access your tenant and start deploying resources in it.</p>



<p>Now, we can move to the thid point which is creating the terraform file. We can start now our preferred editor and create a *.tf file with the following content:</p>



<pre class="wp-block-code"><code>provider "azurerm" {
subscription_id = "kkkkkk"
client_id = "xxxxxx"
client_secret = "yyyyyy"
tenant_id = "zzzzzz"
features {}
}

terraform {
  required_version = "&gt;= 0.13"
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
    }
  }
} </code></pre>



<p>The first block initializes the provider azurerm from terraform, while the second one initializes the terraform file with the required azurerm providrer, and tells terraform where to obtain it and which is the minimum version required. client_id is the same as the appID returned by the az cli when the spn was created.</p>



<p>Then we need another three blocks, one for each resource we want to deploy:</p>



<pre class="wp-block-code"><code>resource "azurerm_resource_group" "rg1" {
name = "TerraformRG"
location = "West Europe"
tags = { Owner = "Albert Nogues" }
}

resource "azurerm_storage_account" "sacc1" {
  name                     = "teststorageacc1"
  resource_group_name      = azurerm_resource_group.rg1.name
  location                 = azurerm_resource_group.rg1.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
}

resource "azurerm_storage_queue" "queue1" {
  name                 = "queue1"
  storage_account_name = azurerm_storage_account.sacc1.name
}</code></pre>



<p>The way terraform blocks work is the following:</p>



<p>resource &#8220;name_of_the_azurerm_resource&#8221; &#8220;our_alias&#8221;{</p>



<p>&#8230; parameters and vars&#8230;</p>



<p>}</p>



<p>We can use then the alias to select things from other blocks we defined previously. So for example, in our example, the storage account, takes the location from the location field defined in the resource group, this way we ensure we create the storage accoun in the same azure location than the resource group (even it&#8217;s not mandatory). An example of how to define an storage account can be found in the official documentation <a href="https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_queue" data-type="URL" data-id="https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_queue" target="_blank" rel="noreferrer noopener">here</a>.</p>



<p>The same with the queue, which is created in the storage_account_name referred by the previous block. We can also put tags to the resources as shown in the resource group block.</p>



<p>Once we are ready, we can launch terraform to create our resources. For security purposes (and if working in multiple environments) it&#8217;s usually not good having in the code the client_id, secret_id, tenant_id and subcription_id. Terraform can read system variables in execution time. These can be set (windows) or exported (linux) with these names:</p>



<pre class="wp-block-code"><code>ARM_SUBSCRIPTION_ID
ARM_CLIENT_ID
ARM_CLIENT_SECRET
ARM_TENANT_ID</code></pre>



<p>Once all ready we can trigger our creation script. We can navigate where our .tf form is and launch it:</p>



<pre class="wp-block-code"><code>terraform plan</code></pre>



<p>Which will give us the changes needed in our azure subscription to acommodate the resources, and it should show us three changes: one for the resource group, another for the storage account and another for the queue:</p>



<pre class="wp-block-code"><code>terraform.exe plan

azurerm_resource_group.rg: Refreshing state... &#91;id=/subscriptions/kkkkkk/resourceGroups/TerraformRG]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_resource_group.rg1 will be created
  + resource "azurerm_resource_group" "rg1" {
      + id       = (known after apply)
      + location = "westeurope"
      + name     = "TerraformRG"
      + tags     = {
          + "Owner" = "Albert Nogues"
        }
    }

  # azurerm_storage_account.sacc1 will be created
  + resource "azurerm_storage_account" "sacc1" {
      + access_tier                      = (known after apply)
      + account_kind                     = "StorageV2"
      + account_replication_type         = "LRS"
      + account_tier                     = "Standard"
      + allow_blob_public_access         = false
      + enable_https_traffic_only        = true
      + id                               = (known after apply)
      + is_hns_enabled                   = false
      + large_file_share_enabled         = (known after apply)
      + location                         = "westeurope"
      + min_tls_version                  = "TLS1_0"
      + name                             = "teststorageacc1"
      + primary_access_key               = (sensitive value)
      + primary_blob_connection_string   = (sensitive value)
      + primary_blob_endpoint            = (known after apply)
      + primary_blob_host                = (known after apply)
      + primary_connection_string        = (sensitive value)
      + primary_dfs_endpoint             = (known after apply)
      + primary_dfs_host                 = (known after apply)
      + primary_file_endpoint            = (known after apply)
      + primary_file_host                = (known after apply)
      + primary_location                 = (known after apply)
      + primary_queue_endpoint           = (known after apply)
      + primary_queue_host               = (known after apply)
      + primary_table_endpoint           = (known after apply)
      + primary_table_host               = (known after apply)
      + primary_web_endpoint             = (known after apply)
      + primary_web_host                 = (known after apply)
      + resource_group_name              = "TerraformRG"
      + secondary_access_key             = (sensitive value)
      + secondary_blob_connection_string = (sensitive value)
      + secondary_blob_endpoint          = (known after apply)
      + secondary_blob_host              = (known after apply)
      + secondary_connection_string      = (sensitive value)
      + secondary_dfs_endpoint           = (known after apply)
      + secondary_dfs_host               = (known after apply)
      + secondary_file_endpoint          = (known after apply)
      + secondary_file_host              = (known after apply)
      + secondary_location               = (known after apply)
      + secondary_queue_endpoint         = (known after apply)
      + secondary_queue_host             = (known after apply)
      + secondary_table_endpoint         = (known after apply)
      + secondary_table_host             = (known after apply)
      + secondary_web_endpoint           = (known after apply)
      + secondary_web_host               = (known after apply)

      + blob_properties {
          + cors_rule {
              + allowed_headers    = (known after apply)
              + allowed_methods    = (known after apply)
              + allowed_origins    = (known after apply)
              + exposed_headers    = (known after apply)
              + max_age_in_seconds = (known after apply)
            }

          + delete_retention_policy {
              + days = (known after apply)
            }
        }

      + identity {
          + principal_id = (known after apply)
          + tenant_id    = (known after apply)
          + type         = (known after apply)
        }

      + network_rules {
          + bypass                     = (known after apply)
          + default_action             = (known after apply)
          + ip_rules                   = (known after apply)
          + virtual_network_subnet_ids = (known after apply)
        }

      + queue_properties {
          + cors_rule {
              + allowed_headers    = (known after apply)
              + allowed_methods    = (known after apply)
              + allowed_origins    = (known after apply)
              + exposed_headers    = (known after apply)
              + max_age_in_seconds = (known after apply)
            }

          + hour_metrics {
              + enabled               = (known after apply)
              + include_apis          = (known after apply)
              + retention_policy_days = (known after apply)
              + version               = (known after apply)
            }

          + logging {
              + delete                = (known after apply)
              + read                  = (known after apply)
              + retention_policy_days = (known after apply)
              + version               = (known after apply)
              + write                 = (known after apply)
            }

          + minute_metrics {
              + enabled               = (known after apply)
              + include_apis          = (known after apply)
              + retention_policy_days = (known after apply)
              + version               = (known after apply)
            }
        }
    }

  # azurerm_storage_queue.queue1 will be created
  + resource "azurerm_storage_queue" "queue1" {
      + id                   = (known after apply)
      + name                 = "queue1"
      + storage_account_name = "teststorageacc1"
    }

<strong><span class="has-inline-color has-vivid-red-color">Plan: 3 to add, 0 to change, 0 to destroy.</span></strong>

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
</code></pre>



<p>Once we confirm this is the change we want and all is in order we can run terraform apply:</p>



<pre class="wp-block-code"><code>terraform.exe apply
...
...
...
Plan: 3 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: <strong>yes</strong></code></pre>



<p>We say yes and let&#8217;s go!</p>



<p>It may happen that there is some error, like a taken name, in this case we will get an error:</p>



<pre class="wp-block-code"><code>Error: Error creating Azure Storage Account "teststorageacc1": storage.AccountsClient#Create: Failure sending request: StatusCode=0 -- Original Error: autorest/azure: Service returned an error. Status=&lt;nil&gt; Code="StorageAccountAlreadyTaken" Message="The storage account named teststorageacc1 is already taken."

  on terraformTest.tf line 48, in resource "azurerm_storage_account" "sacc1":
  48: resource "azurerm_storage_account" "sacc1" {
</code></pre>



<p>We can fix it and rerun again, and the resources that were created already will not be modified (like in this case the resource group).</p>



<pre class="wp-block-code"><code>azurerm_storage_account.sacc1: Creating...
azurerm_storage_account.sacc1: Still creating... &#91;10s elapsed]
azurerm_storage_account.sacc1: Still creating... &#91;20s elapsed]
azurerm_storage_account.sacc1: Creation complete after 21s &#91;id=/subscriptions/kkkkkk/resourceGroups/TerraformRG/providers/Microsoft.Storage/storageAccounts/teststorageacc1anogues]
azurerm_storage_queue.queue1: Creating...
azurerm_storage_queue.queue1: Creation complete after 0s &#91;id=https://teststorageacc1anogues.queue.core.windows.net/queue1]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.</code></pre>



<p>Now we can either call the cli or browse through the azure portal to confirm that the storage account was created and the queue inside it:</p>



<pre class="wp-block-code"><code><strong>az storage account list</strong>
&#91;
  {
    "accessTier": "Hot",
    "allowBlobPublicAccess": false,
    "azureFilesIdentityBasedAuthentication": null,
    "blobRestoreStatus": null,
    "creationTime": "2020-12-23T18:30:49.007107+00:00",
    "customDomain": null,
    "enableHttpsTrafficOnly": true,
    "encryption": {
      "keySource": "Microsoft.Storage",
      "keyVaultProperties": null,
      "requireInfrastructureEncryption": null,
      "services": {
        "blob": {
          "enabled": true,
          "keyType": "Account",
          "lastEnabledTime": "2020-12-23T18:30:49.085263+00:00"
        },
        "file": {
          "enabled": true,
          "keyType": "Account",
          "lastEnabledTime": "2020-12-23T18:30:49.085263+00:00"
        },
        "queue": null,
        "table": null
      }
    },
    "failoverInProgress": null,
    "geoReplicationStats": null,
    "id": "/subscriptions/kkkkkk/resourceGroups/TerraformRG/providers/Microsoft.Storage/storageAccounts/teststorageacc1anogues",
    "identity": null,
    "isHnsEnabled": false,
    "kind": "StorageV2",
    "largeFileSharesState": null,
    "lastGeoFailoverTime": null,
    "location": "westeurope",
    "minimumTlsVersion": "TLS1_0",
    "name": "<strong><span class="has-inline-color has-vivid-red-color">teststorageacc1anogues</span></strong>",
    "networkRuleSet": {
      "bypass": "AzureServices",
      "defaultAction": "Allow",
      "ipRules": &#91;],
      "virtualNetworkRules": &#91;]
    },
    "primaryEndpoints": {
      "blob": "https://teststorageacc1anogues.blob.core.windows.net/",
      "dfs": "https://teststorageacc1anogues.dfs.core.windows.net/",
      "file": "https://teststorageacc1anogues.file.core.windows.net/",
      "internetEndpoints": null,
      "microsoftEndpoints": null,
      "queue": "https://teststorageacc1anogues.queue.core.windows.net/",
      "table": "https://teststorageacc1anogues.table.core.windows.net/",
      "web": "https://teststorageacc1anogues.z6.web.core.windows.net/"
    },
    "primaryLocation": "westeurope",
    "privateEndpointConnections": &#91;],
    "provisioningState": "Succeeded",
    "resourceGroup": "TerraformRG",
    "routingPreference": null,
    "secondaryEndpoints": null,
    "secondaryLocation": null,
    "sku": {
      "name": "Standard_LRS",
      "tier": "Standard"
    },
    "statusOfPrimary": "available",
    "statusOfSecondary": null,
    "tags": {},
    "type": "Microsoft.Storage/storageAccounts"
  }
]
</code></pre>



<p>And then confirming the storage account has been created we can check the queue inside:</p>



<pre class="wp-block-code"><code>az storage queue list --account-name teststorageacc1anogues

There are no credentials provided in your command and environment, we will query for the account key inside your storage account.
Please provide --connection-string, --account-key or --sas-token as credentials, or use `--auth-mode login` if you have required RBAC roles in your command. For more information about RBAC roles in storage, visit https://docs.microsoft.com/en-us/azure/storage/common/storage-auth-aad-rbac-cli.
Setting the corresponding environment variables can avoid inputting credentials in your command. Please use --help to get more information.
&#91;
  {
    "approximateMessageCount": null,
    "metadata": null,
    "name": "<strong><span class="has-inline-color has-vivid-red-color">queue1</span></strong>"
  }
]</code></pre>



<p>And we confirmed all is ready for our second part!</p>



<p>It&#8217;s time to move to azure-storage-queue python library for our 5th step. There are different ways to authenticate to the queue. Either we use the QueueServiceClient or the QueueService. the differences between both are in their official documentation <a rel="noreferrer noopener" href="https://pypi.org/project/azure-storage-queue/" data-type="URL" data-id="https://pypi.org/project/azure-storage-queue/" target="_blank">here</a>.</p>



<p>The easiest way albeit not the recommended one is to use the queue key, or even better, we can grab the connection string directly from the queue keys section. Be aware that this allows to do virtually all with that queue. So don&#8217;t lose this key, and if you do, rotate them and generate new ones.</p>



<p>Just open a new python file and to write a sync message use the following code:</p>



<pre class="wp-block-code"><code>from azure.storage.queue import QueueClient

queue = QueueClient.from_connection_string(conn_str="DefaultEndpointsProtocol=https;AccountName=teststorageacc1anogues;AccountKey=<strong><span class="has-inline-color has-vivid-red-color">xxxxxx</span></strong>;EndpointSuffix=core.windows.net", queue_name="<strong><span class="has-inline-color has-vivid-cyan-blue-color">queue1</span></strong>")

queue.send_message("Hello World!")</code></pre>



<p>This will send a Hello World message to our queue. If we print the output received we should see some debug info like the following:</p>



<pre class="wp-block-code"><code>{'id': 'a489914f-cc7e-4504-9feb-33b0efce6587', 'inserted_on': datetime.datetime(2020, 12, 24, 19, 21, 20, tzinfo=datetime.timezone.utc), 'expires_on': datetime.datetime(2020, 12, 31, 19, 21, 20, tzinfo=datetime.timezone.utc), 'dequeue_count': None, 'content': 'Hello World!', 'pop_receipt': 'AgAAAAMAAAAAAAAA727vbgja1gE=', 'next_visible_on': datetime.datetime(2020, 12, 24, 19, 21, 20, tzinfo=datetime.timezone.utc)}</code></pre>



<p>Now if we do not believe the message has arribed properly to the queue, we can go to the azure portal and check by ourselves. For this, we find ou storage account, click in queues and on queue1 (or whatever name you choose in terraform) and you should see the message along with the arrival time and the expiry time (by default, 7 days from now on)</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="833" height="311" src="https://www.albertnogues.com/wp-content/uploads/2020/12/QueueMessage.png" alt="" class="wp-image-962" srcset="https://www.albertnogues.com/wp-content/uploads/2020/12/QueueMessage.png 833w, https://www.albertnogues.com/wp-content/uploads/2020/12/QueueMessage-300x112.png 300w, https://www.albertnogues.com/wp-content/uploads/2020/12/QueueMessage-768x287.png 768w, https://www.albertnogues.com/wp-content/uploads/2020/12/QueueMessage-161x60.png 161w" sizes="auto, (max-width: 833px) 100vw, 833px" /></figure>



<p>Let&#8217;s see how to retrieve that message from python now. Remember that storage account queues do not delete the processed messages so If you do not delete them they will stay until expiration (even they will stay &#8220;hidden&#8221; for 90 seconds by default unless you pass other parameter) , so make sure your call deletes the message once processed (in this case read):</p>



<pre class="wp-block-code"><code>messages = queue.receive_messages()

for msg in messages:
    print(msg.content)
    queue.delete_message(msg)</code></pre>



<p>And this will output our message:</p>



<pre class="wp-block-code"><code>Hello World!</code></pre>



<p><em>If you want to only see your message without actually &#8220;reading it&#8221; you can use the <strong>peek </strong>function. This will not mark your message as &#8220;seen&#8221; and will be returned by any other consumer reading the queue or any retry you do.</em></p>



<p>If we go back to the azure portal and check the queue we should see the message is not there anymore. We can also delete the messages from the azure portal,a s well as create them directly there.</p>



<p>To improve our code a little bit we can use SAS keys instead of the storage account master keys. You can generate a SAS token directly from python with the following code:</p>



<pre class="wp-block-code"><code>from datetime import datetime, timedelta
from azure.storage.queue import QueueServiceClient, generate_account_sas, ResourceTypes, AccountSasPermissions

sas_token = generate_account_sas(
    account_name="&lt;storage-account-name&gt;",
    account_key="&lt;account-access-key&gt;",
    resource_types=ResourceTypes(service=True),
    permission=AccountSasPermissions(read=True),
    expiry=datetime.utcnow() + timedelta(hours=1)
)

queue_service_client = QueueServiceClient(account_url="https://&lt;my_account_name&gt;.queue.core.windows.net", credential=sas_token)</code></pre>



<p>But you still need to have your credentials inside the code. So the safest way to manage it is to create the SAS token from somewhere else with a caducity not long in the future and use this in your code, or use environment variables for passing sensitive information to your code. With the os library in python you can read them and use inside your code, without having to be written there and exposing them in your code or git.</p>



<p>For more information you can check microsoft official documentation <a rel="noreferrer noopener" href="https://docs.microsoft.com/en-us/azure/storage/queues/storage-python-how-to-use-queue-storage?tabs=python" data-type="URL" data-id="https://docs.microsoft.com/en-us/azure/storage/queues/storage-python-how-to-use-queue-storage?tabs=python" target="_blank">here</a>, the quickstart guide <a href="https://docs.microsoft.com/en-us/azure/storage/queues/storage-quickstart-queues-python" data-type="URL" data-id="https://docs.microsoft.com/en-us/azure/storage/queues/storage-quickstart-queues-python" target="_blank" rel="noreferrer noopener">here </a>or the pypi page <a href="https://pypi.org/project/azure-storage-queue/" data-type="URL" data-id="https://pypi.org/project/azure-storage-queue/" target="_blank" rel="noreferrer noopener">here</a>.</p>
<p>The post <a href="https://www.albertnogues.com/deploying-and-interacting-with-an-azure-storage-account-and-a-queue-with-terraform-and-python-part-i/">Deploying an Azure Storage Account with a queue with terraform and python</a> appeared first on <a href="https://www.albertnogues.com">Albert Nogués</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
