<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Blog | Drew Skwiers-Koballa</title>
 <link href="https://blog.drewsk.tech/atom.xml" rel="self"/>
 <link href="https://blog.drewsk.tech"/>
 <updated>2026-07-05T20:21:18+00:00</updated>
 <id>https://blog.drewsk.tech</id>
 <author>
   <name>Drew Skwiers-Koballa</name>
   <email>drew@drewsk.tech</email>
 </author>

 
 <entry>
   <title>Code analysis with SQL projects</title>
   <link href="https://blog.drewsk.tech/blog/2025/01/22/code-analysis-with-sql-projects/"/>
   <updated>2025-01-22T00:00:00+00:00</updated>
   <id>https://blog.drewsk.tech/blog/2025/01/22/code-analysis-with-sql-projects</id>
   <content type="html">&lt;p&gt;SQL projects are a development capability for SQL Server and Azure SQL where your database objects are stored as code with integrations for CI/CD capabilities like code quality checks and dynamically calculated deployments. This article walks/runs through the fundamentals of code analysis with SQL projects and how it is useful both for local development and as a CI check.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/generated/assets/images/sql-projects-banner-800-777c3dca9.png&quot; srcset=&quot;/generated/assets/images/sql-projects-banner-400-777c3dca9.png 400w, /generated/assets/images/sql-projects-banner-600-777c3dca9.png 600w, /generated/assets/images/sql-projects-banner-800-777c3dca9.png 800w, /generated/assets/images/sql-projects-banner-1000-777c3dca9.png 1000w&quot; /&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;️-before-you-get-started&quot;&gt;⏸️ Before you get started&lt;/h2&gt;

&lt;p&gt;In a previous article, &lt;a href=&quot;https://dev.to/drewsk/sql-database-projects-intro-52hg&quot;&gt;“SQL database projects intro”&lt;/a&gt;, we created a SQL project with 2 tables and a stored procedure.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;dbo.Product (table)&lt;/li&gt;
  &lt;li&gt;dbo.ProductOrder (table)&lt;/li&gt;
  &lt;li&gt;dbo.GetProductInfo (stored procedure)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SQL database projects are built on top of the .NET SDK - you don’t need to know .NET development to use them - but you do need the &lt;a href=&quot;https://dotnet.microsoft.com/download&quot;&gt;.NET 8 or higher SDK&lt;/a&gt; installed.&lt;/p&gt;

&lt;p&gt;Want to quickly check if you have the .NET SDK installed?&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet &lt;span class=&quot;nt&quot;&gt;--list-sdks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;️-with-the-net-sdk-installed-and-a-sample-project-created-like-the-one-from-the-intro-article-youre-ready-to-go&quot;&gt;▶️ With the .NET SDK installed and a sample project created like the one from the intro article, you’re ready to go.&lt;/h3&gt;

&lt;h2 id=&quot;-create-a-new-view&quot;&gt;📝 Create a new view&lt;/h2&gt;

&lt;p&gt;Add a new view &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[dbo].[All Products]&lt;/code&gt; in a &lt;em&gt;.sql&lt;/em&gt; file (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.AllProducts.sql&lt;/code&gt;) in a new folder &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Views&lt;/code&gt; under project folder.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;VIEW&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;All&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Products&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Build the SQL project, verifying the object references and our SQL syntax is correct.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;️-check-our-code-quality&quot;&gt;🛠️ Check our code quality&lt;/h2&gt;

&lt;p&gt;While &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT *&lt;/code&gt; seems like a nice shortcut to reference all potential columns, it comes with risks of schema drift, inadvertently clogging up I/O with extra data, and more. In general - your code is more fragile in ways that aren’t easily observed.&lt;/p&gt;

&lt;p&gt;This is where SQL code analysis comes in handy, because it will check the database model created from our SQL project for detectable issues. We can run code analysis on our SQL project as a 1-off analysis with an extra property on the project build &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/p:RunSqlCodeAnalysis=true&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet build /p:RunSqlCodeAnalysis&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now instead of the build succeeding with no warnings, we have two warnings:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;warning SR0011: Microsoft.Rules.Data : Object name(All Products) contains special characters.&lt;/li&gt;
  &lt;li&gt;warning SR0001 Microsoft.Rules.Data : The shape of the result set produced by a SELECT * statement will change if the underlying table or view structure changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Getting ongoing analysis of our SQL code quality provides a quick feedback loop even before we send the code into a git repository. We can enable code analysis to run by default on every project build by incorporating that property into the project file itself with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;RunSqlCodeAnalysis&amp;gt;&lt;/code&gt;. A sample project file with the code analysis setting added would look like:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;DefaultTargets=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Build&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;Sdk&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Build.Sql&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.0-rc1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Name&amp;gt;&lt;/span&gt;ProductsTutorial&lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;/Name
&lt;span class=&quot;nt&quot;&gt;&amp;lt;DSP&amp;gt;&lt;/span&gt;Microsoft.Data.Tools.Schema.Sql.Sql160DatabaseSchemaProvider&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DSP&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ModelCollation&amp;gt;&lt;/span&gt;1033, CI&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ModelCollation&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RunSqlCodeAnalysis&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/RunSqlCodeAnalysis&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;-choose-errors-and-warnings&quot;&gt;🚧 Choose errors and warnings&lt;/h2&gt;

&lt;p&gt;Some code issues are worse than others - they might have a high impact or be difficult to change in the future. For these kinds of issues we can elevate the warning to an error that will cause our build to fail.&lt;/p&gt;

&lt;p&gt;In our current project we’re getting a warning that the object name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[dbo].[All Products]&lt;/code&gt; contains a special character (the space). Before I fix this issue I want to make sure that these kinds of mistakes don’t get ignored in the future.&lt;/p&gt;

&lt;p&gt;The project property &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;SqlCodeAnalysisRules&amp;gt;&lt;/code&gt; can be used to exclude a rule completely or elevate a rule from a warning to an error. When choosing errors and warnings, the project property is a semicolon separated list with three settings available for each rule:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;elevate a rule from warning to error with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+!&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;no entry for the rule results in the rule throwing a warning&lt;/li&gt;
  &lt;li&gt;demote a rule to no warning with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt; (disable)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The setting per rule is prefixed to the rule name. Let’s turn the object name warning into an error message since names can be more difficult to change down the road. Right below the property &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;RunSqlCodeAnalysis&amp;gt;&lt;/code&gt; in our project file we add:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;SqlCodeAnalysisRules&amp;gt;&lt;/span&gt;+!Microsoft.Rules.Data.SR0011;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SqlCodeAnalysisRules&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we build our project again using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; we recieve one error and one warning:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;error SR0011: Microsoft.Rules.Data : Object name(All Products) contains special characters.&lt;/li&gt;
  &lt;li&gt;warning SR0001 Microsoft.Rules.Data : The shape of the result set produced by a SELECT * statement will change if the underlying table or view structure changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⚠️ Note that when the build fails, the build artifact (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.dacpac&lt;/code&gt; file) isn’t updated. You need a successful build for your database model to be ready for deployment.&lt;/p&gt;

&lt;h3 id=&quot;️-fix-the-view-name&quot;&gt;✏️ Fix the view name&lt;/h3&gt;

&lt;p&gt;Using the information in the error message, including the filename and line number, we’ll fix the build error resulting from our modification to the code analysis warnings and errors. We should edit the view definition in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.AllProducts.sql&lt;/code&gt; to have a name without spaces, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[dbo].[AllProducts]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once the view name is fixed, verify that your project now builds with a single warning message for SR0001 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT *&lt;/code&gt;).&lt;/p&gt;

&lt;h3 id=&quot;-supress-a-specific-code-analysis-finding&quot;&gt;📍 Supress a specific code analysis finding&lt;/h3&gt;

&lt;p&gt;Code analysis rules provide a structure for evaluating your code, but at the end of the day its your project and you may have a reason for contradicting best practices. Instead of completely disabling a rule with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;SqlCodeAnalysisRules&amp;gt;&lt;/code&gt; project property, we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StaticCodeAnalysis.SuppressMessages.xml&lt;/code&gt; file to suppress a specific rule for a specific file.&lt;/p&gt;

&lt;p&gt;Create a new file next to the SQL project file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StaticCodeAnalysis.SuppressMessages.xml&lt;/code&gt;. In this file we add an entry for the view file and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT *&lt;/code&gt; rule.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;StaticCodeAnalysis&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:Microsoft.Data.Tools.Schema.StaticCodeAnalysis&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;SuppressedFile&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;FilePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Views/dbo.AllProducts.sql&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;SuppressedRule&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Category=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.Rules.Data&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;RuleId=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SR0001&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/SuppressedFile&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/StaticCodeAnalysis&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT *&lt;/code&gt; in other files of our project the warning will appear for those instances, but we’ve specified that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT *&lt;/code&gt; is allowed in our view &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.AllProducts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; with this message suppression XML file in the project will result in no warnings being shown for our project now.&lt;/p&gt;

&lt;h2 id=&quot;-optional-incorporate-additional-code-analysis-rules&quot;&gt;🧩 (optional) Incorporate additional code analysis rules&lt;/h2&gt;

&lt;p&gt;The small set of code analysis rules included with SQL projects are helpful, but there are quite a few community projects &lt;a href=&quot;https://learn.microsoft.com/sql/tools/sql-database-projects/concepts/code-analysis-extensibility&quot;&gt;extending&lt;/a&gt; the code analysis rules. Incorporating these custom code analysis rulesets into your SQL project code analysis is quickly accomplished through package references.&lt;/p&gt;

&lt;p&gt;A package reference is an entry in the SQL project file to a NuGet feed, like the community &lt;strong&gt;TSQLSmells&lt;/strong&gt; on NuGet.org - https://www.nuget.org/packages/ErikEJ.DacFX.TSQLSmellSCA&lt;/p&gt;

&lt;p&gt;We don’t even need to manualy edit the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.sqlproj&lt;/code&gt; because the .NET tooling has a terminal command to add packages:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet add package ErikEJ.DacFX.TSQLSmellSCA &lt;span class=&quot;nt&quot;&gt;--version&lt;/span&gt; 1.1.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When we run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; again, the build succeeds but we now have two new warnings!&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;warning SML005: Smells : Avoid use of ‘Select *’&lt;/li&gt;
  &lt;li&gt;warning SML030: Smells : Include SET NOCOUNT ON inside stored procedures&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While adding the custom rules to your SQL project takes just a few moments, the developers that work on the rulesets have invested countless hours of their time and share with the community. Remember to contribute to the projects that benefit your work as you are able.&lt;/p&gt;

&lt;h2 id=&quot;-optional-add-a-github-continuous-integration-ci-pipeline&quot;&gt;🚀 (optional) Add a GitHub continuous integration (CI) pipeline&lt;/h2&gt;

&lt;p&gt;If we’ve pushed our project to a GitHub repository, we can utilize GitHub Actions to check our project for code analysis findings. This is a great way to automate watching for code smells in databases.&lt;/p&gt;

&lt;p&gt;The pipeline definitions are stored in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github/workflows&lt;/code&gt; folder. Create a new file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;code-analysis.yml&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github/workflows&lt;/code&gt; folder.  An example pipeline definition is shown below to get you started, but your branching strategy and code review process may vary.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# .github/workflows/code-analysis.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Run code analysis on SQL project&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;main&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;build-with-analysis&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v4&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Setup .NET&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/setup-dotnet@v4&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;dotnet-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;8.0.x&lt;/span&gt;

    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Build with code analysis&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet build -warnaserror ProductsTutorial.sqlproj /p:RunSqlCodeAnalysis=true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this workflow we’ve added &lt;em&gt;yet another&lt;/em&gt; flag to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; so that all code analysis warnings are elevated to errors. This is useful when you are using code analysis as an optional check. If you’re using code analysis as a required check (highly suggested) selecting specific rules to throw errors instead via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SqlCodeAnalysisRules&lt;/code&gt; project property is a better approach.&lt;/p&gt;

&lt;p&gt;Guess what!? Your code analysis CI check is also doing the basic model validation for the database - ensuring you’re not referencing objects that don’t exist and the SQL syntax is valid. That’s a win-win.&lt;/p&gt;

&lt;h2 id=&quot;-learn-more&quot;&gt;📚 Learn more&lt;/h2&gt;

&lt;p&gt;The tooling ecosystem around SQL projects includes VS Code, Visual Studio, and the SqlPackage CLI. Here are some additional resources to learn more about SQL projects for development and deployment:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://aka.ms/sqlprojects&quot;&gt;Microsoft.Build.Sql projects documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://aka.ms/sqlprojects-samples&quot;&gt;SQL projects CI/CD sample repository&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://aka.ms/sqlpackage-ref&quot;&gt;SqlPackage for command line deployments&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Introduction to SQL projects</title>
   <link href="https://blog.drewsk.tech/blog/2024/12/13/intro-to-sql-projects/"/>
   <updated>2024-12-13T00:00:00+00:00</updated>
   <id>https://blog.drewsk.tech/blog/2024/12/13/intro-to-sql-projects</id>
   <content type="html">&lt;p&gt;SQL projects are a development capability for SQL Server and Azure SQL where your database objects are stored as code with integrations for CI/CD capabilities like code quality checks and dynamically calculated deployments. This article walks/runs through creating a new SQL project and deploying it to a database from the command line and any text editor. The tooling ecosystem around SQL projects includes VS Code, Visual Studio, and the SqlPackage CLI. Learn more about all of this at the &lt;a href=&quot;https://aka.ms/sqlprojects&quot;&gt;Microsoft.Build.Sql projects documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/generated/assets/images/sql-projects-banner-800-777c3dca9.png&quot; srcset=&quot;/generated/assets/images/sql-projects-banner-400-777c3dca9.png 400w, /generated/assets/images/sql-projects-banner-600-777c3dca9.png 600w, /generated/assets/images/sql-projects-banner-800-777c3dca9.png 800w, /generated/assets/images/sql-projects-banner-1000-777c3dca9.png 1000w&quot; /&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;️-before-you-get-started&quot;&gt;⏸️ Before you get started&lt;/h2&gt;

&lt;p&gt;SQL database projects are built on top of the .NET SDK - you don’t need to know .NET development to use them - but you do need the &lt;a href=&quot;https://dotnet.microsoft.com/download&quot;&gt;.NET 8 or higher SDK&lt;/a&gt; installed.&lt;/p&gt;

&lt;p&gt;Want to quickly check if you have the .NET SDK installed?&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet &lt;span class=&quot;nt&quot;&gt;--list-sdks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The SqlPackage CLI is an indespensible part of database deployments because it will automatically calculate the actual scripts necessary to modify a new or existing database to match the contents of your SQL project. SqlPackage is available for Windows, macOS, and Linux all as a .NET tool, so you can install it quickly in a single command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet tool &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; microsoft.sqlpackage
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;️-with-the-net-sdk-and-the-sqlpackage-cli-both-installed-youre-ready-to-go&quot;&gt;▶️ With the .NET SDK and the SqlPackage CLI both installed, you’re ready to go.&lt;/h3&gt;

&lt;h2 id=&quot;-create-a-sql-project&quot;&gt;📁 Create a SQL project&lt;/h2&gt;

&lt;p&gt;We’ll use the &lt;a href=&quot;https://www.nuget.org/packages/Microsoft.build.sql.templates&quot;&gt;Microsoft.Build.Sql.Templates NuGet package&lt;/a&gt; to get started with a new SQL project.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet new &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;Microsoft.Build.Sql.Templates
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Create a new SQL project using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sqlproj&lt;/code&gt; template:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet new sqlproj &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; ProductsTutorial
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;-add-a-database-object&quot;&gt;📝 Add a database object&lt;/h2&gt;

&lt;p&gt;Add a new table &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Product&lt;/code&gt; in a &lt;em&gt;.sql&lt;/em&gt; file (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Product.sql&lt;/code&gt;) alongside the project file.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nvarchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;️-build-the-project&quot;&gt;🛠️ Build the project&lt;/h2&gt;

&lt;p&gt;The SQL project (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.sqlproj&lt;/code&gt;) is the source code for our database definition, and we would use a build artifact to deploy to a database instance (such as a local container or a SQL Server). 
Build the project to create a .dacpac file.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;-add-more-database-objects&quot;&gt;✚ Add more database objects&lt;/h2&gt;

&lt;h3 id=&quot;add-a-stored-procedure&quot;&gt;Add a stored procedure&lt;/h3&gt;
&lt;p&gt;We’re going to add a stored procedure to the project. Create a new file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.GetProductInfo.sql&lt;/code&gt; alongside the project file.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PROCEDURE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetProductInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductID&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;BEGIN&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductID&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductName&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SUM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TotalQuantity&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;LEFT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;JOIN&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductOrder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PO&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductID&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductID&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;P&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductName&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;END&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;🚧 We’ve added a stored procedure that references the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Product&lt;/code&gt; table we created earlier in addition to a table we haven’t created yet (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.ProductOrder&lt;/code&gt;). Running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; at this point returns build warnings about unresolved references. This is good - we want our development tooling to help us create valid SQL objects.&lt;/p&gt;

&lt;h3 id=&quot;adding-the-missing-table&quot;&gt;Adding the missing table&lt;/h3&gt;

&lt;p&gt;Create a new file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.ProductOrder.sql&lt;/code&gt; alongside the project file for the missing table &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.ProductOrder&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductOrderID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;organize-the-project&quot;&gt;Organize the project&lt;/h3&gt;

&lt;p&gt;We’ve been creating all our files in the same folder with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.sqlproj&lt;/code&gt; file. This works, but it’s not the best way to organize a project. A common approach is to group objects by schema and/or object type.&lt;/p&gt;

&lt;p&gt;Create a new folder &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tables&lt;/code&gt; and move the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Product.sql&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.ProductOrder.sql&lt;/code&gt; files into it. Create a new folder &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StoredProcedures&lt;/code&gt; and move the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.GetProductInfo.sql&lt;/code&gt; file into it.&lt;/p&gt;

&lt;p&gt;When you run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; again, the build still succeeds. The build process automatically finds the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.sql&lt;/code&gt; files in the project folder and subfolders.&lt;/p&gt;

&lt;h3 id=&quot;-use-your-existing-databases&quot;&gt;💡 Use your existing databases&lt;/h3&gt;

&lt;p&gt;If you have an existing database, the command line tool SqlPackage can extract object definitions for the whole database.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sqlpackage /Action:Extract /TargetFile:newfolderforfiles /SourceConnectionString:&lt;span class=&quot;s2&quot;&gt;&quot;Data Source=yourservername;Initial Catalog=yourdatabasename;Authentication=Active Directory Interactive&quot;&lt;/span&gt; /p:ExtractTarget&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;SchemaObjectType
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;️-publish-a-sql-project&quot;&gt;🛳️ Publish a SQL project&lt;/h2&gt;

&lt;p&gt;Publish a SQL project to a database using the SqlPackage &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;publish&lt;/code&gt; command. Learn more about the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;publish&lt;/code&gt; command in the &lt;a href=&quot;https://learn.microsoft.com/sql/tools/sqlpackage/sqlpackage-publish&quot;&gt;SqlPackage documentation&lt;/a&gt;, where additional examples and details on the parameters are available.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# example publish to Azure SQL Database using SQL authentication and a connection string&lt;/span&gt;
SqlPackage /Action:Publish /SourceFile:&lt;span class=&quot;s2&quot;&gt;&quot;bin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\D&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ebug&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\P&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;roductsTutorial.dacpac&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    /TargetConnectionString:&lt;span class=&quot;s2&quot;&gt;&quot;Server=tcp:{yourserver}.database.windows.net,1433;Initial Catalog=ProductsTutorial;User ID=sqladmin;Password={your_password};Encrypt=True;TrustServerCertificate=False;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When the publish command completes, you can connect to the database and view the objects that were created.&lt;/p&gt;

&lt;h3 id=&quot;-optional-update-the-project-and-publish-again&quot;&gt;🧩 (optional) Update the project and publish again&lt;/h3&gt;
&lt;p&gt;We can update the project by adding a new column to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Product&lt;/code&gt; table. Open the existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dbo.Product.sql&lt;/code&gt; file and add column for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ProductDescription&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The resulting file is:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dbo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nvarchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProductDescription&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nvarchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;800&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When we run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet build&lt;/code&gt; again, the build succeeds and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ProductsTutorial.dacpac&lt;/code&gt; file is updated.&lt;/p&gt;

&lt;p&gt;🪄 We can publish the updated project to the database using the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;publish&lt;/code&gt; command as before.  Running the publish command again will update the existing database schema automatically, there’s no need to remove the database first or track the changes manually.&lt;/p&gt;

&lt;h2 id=&quot;optional-check-the-project-into-source-control&quot;&gt;(optional) Check the project into source control&lt;/h2&gt;

&lt;p&gt;One of the benefits of using SQL projects is that the database schema is stored in source control. This makes it easy to collaborate and to deploy changes using CI/CD practices.&lt;/p&gt;

&lt;p&gt;When we build the project, the .dacpac file and other artifacts are created in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;obj&lt;/code&gt; folders. We can keep these folders out of source control by adding them to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitignore&lt;/code&gt; file or by using the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitignore&lt;/code&gt; for .NET.&lt;/p&gt;

&lt;p&gt;The default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitignore&lt;/code&gt; for .NET projects can be created by running the following command in the project folder:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet new gitignore
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once you’ve committed the project to source control, you can push it to a GitHub/remote repository.  You can do this from your IDE or from the command line using the following commands:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Commit the project to source control&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git init
git add &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;new project&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Push the project to a GitHub repository&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git remote add origin https://github.com/&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;yourusername&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;yourrepository&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;.git
git branch &lt;span class=&quot;nt&quot;&gt;-M&lt;/span&gt; main
git push &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;-optional-add-a-github-builddeploy-pipeline&quot;&gt;🚀 (optional) Add a GitHub build/deploy pipeline&lt;/h2&gt;

&lt;p&gt;If we’ve pushed our project to a GitHub repository, we can utilize GitHub Actions to build and deploy our project to a database. This is a great way to automate the deployment of database changes.&lt;/p&gt;

&lt;p&gt;The pipeline definitions are stored in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github/workflows&lt;/code&gt; folder. Create a new file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build-and-deploy.yml&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github/workflows&lt;/code&gt; folder.  We’ll use &lt;a href=&quot;https://github.com/azure/sql-action&quot;&gt;sql-action&lt;/a&gt; to handle building the SQL project and publishing it to a database. It is recommended you check out the &lt;a href=&quot;https://github.com/azure/sql-action&quot;&gt;sql-action documentation&lt;/a&gt; for more information on how to use it, but an example pipeline definition is shown below to get you started.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The example pipeline definition below uses a &lt;a href=&quot;https://docs.github.com/actions/reference/encrypted-secrets&quot;&gt;GitHub secret&lt;/a&gt; to store the connection string as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SQL_CONNECTION_STRING&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# .github/workflows/build-and-deploy.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Build and deploy SQL project&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v3&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;azure/sql-action@v2.3&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;        
        &lt;span class=&quot;na&quot;&gt;connection-string&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;./ProductsTutorial.sqlproj&apos;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;publish&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;-learn-more&quot;&gt;📚 Learn more&lt;/h2&gt;

&lt;p&gt;Wow, you made it to the end!  Here are some additional resources to learn more about SQL projects for development and deployment:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://aka.ms/sqlprojects&quot;&gt;Microsoft.Build.Sql projects documentation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://aka.ms/sqlprojects-samples&quot;&gt;SQL projects CI/CD sample repository&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://aka.ms/sqlpackage-ref&quot;&gt;SqlPackage for command line deployments&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Connecting to SQL Server on macOS from Windows VM</title>
   <link href="https://blog.drewsk.tech/blog/2024/01/15/connecting-sql-vm-macos/"/>
   <updated>2024-01-15T00:00:00+00:00</updated>
   <id>https://blog.drewsk.tech/blog/2024/01/15/connecting-sql-vm-macos</id>
   <content type="html">&lt;p&gt;There are a few instances where you want to run SQL Server on macOS and interact with it from a windows VM. As someone that works within the world of SQL Server/Azure SQL, the use of SQL Server Management Studio (SSMS) comes to mind immediately - for the tasks you can’t complete in Azure Data Studio on macOS directly.  Another adjacent (and generally unfortunate scenario) is when work with .NET Framework is required, and for that, you &lt;em&gt;need&lt;/em&gt; Windows. This article will step through the network information needed to connect from a Windows VM on macOS to a SQL Server container.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/generated/assets/images/macos-windows-vm-800-fc0d2f39a.png&quot; srcset=&quot;/generated/assets/images/macos-windows-vm-400-fc0d2f39a.png 400w, /generated/assets/images/macos-windows-vm-600-fc0d2f39a.png 600w, /generated/assets/images/macos-windows-vm-800-fc0d2f39a.png 800w, /generated/assets/images/macos-windows-vm-1000-fc0d2f39a.png 1000w&quot; /&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;a-few-basics&quot;&gt;A few basics&lt;/h2&gt;

&lt;p&gt;I want to rehash a few things quickly as to why we have this specific setup - a macOS host machine running both a container runtime (Docker) and Parallels for VM hosting.  The arm64 CPU architecture of modern macOS machines as part of their SoC (system on a chip) hardware is just about magical (not a technical term) compared to a lot of other contemporary machines, especially factoring in the power sipping consumption, but a number of components are not natively available. SQL Server, for example, is only available as an x64 installer for Windows/Linux and an x64 container.&lt;/p&gt;

&lt;p&gt;Operating systems are able to offer integrated emulation layers, such that they can run applications that don’t match their CPU architecture.  For example, Windows on arm64 can run the x64 SQL Server Management Studio with a slight performance hit due to the emulation layer.  On macOS, the emulation layer is able to interact with the Docker container runtime and run containers built for x64.&lt;/p&gt;

&lt;p&gt;Your best bet for running a Windows VM on macOS is to run an arm64 image of Windows in Parallels, and within the VM you can run the Windows applications you need - except SQL Server itself.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://drewsktech.blob.core.windows.net/images/2024/January/container-create.png&quot; width=&quot;300px&quot; alt=&quot;Creating a SQL Server container on macOS&quot; /&gt;
  &lt;figcaption&gt;Creating a SQL Server container on macOS&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;vm-networking&quot;&gt;VM networking&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://kb.parallels.com/4948&quot;&gt;Shared networking&lt;/a&gt; is the recommended (and default) network setting in Parallels.  In this network layout, your macOS machine is automatically sharing its network connection with the VM and this is usually sufficient for VM use.  The other insight we need to connect between macOS and the Windows VM is knowledge of the &lt;em&gt;virtual subnet&lt;/em&gt; (network) between the macOS host and the VM.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://drewsktech.blob.core.windows.net/images/2024/January/shared-network.png&quot; width=&quot;300px&quot; alt=&quot;Confirming the VM has shared network set&quot; /&gt;
  &lt;figcaption&gt;Confirming the VM has shared network set&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The fastest way I’ve found to get my network info for the VM is within the VM itself, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ipconfig&lt;/code&gt; from Windows terminal.  From its output, I note the VM’s IP address and its gateway address.  On my machine, the VM has the address &lt;strong&gt;10.211.55.3&lt;/strong&gt; and the gateway is &lt;strong&gt;10.211.55.1&lt;/strong&gt;.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://drewsktech.blob.core.windows.net/images/2024/January/windows-network-config.png&quot; width=&quot;400px&quot; alt=&quot;ipconfig showing my VM IP address is 10.211.55.3&quot; /&gt;
  &lt;figcaption&gt;ipconfig showing my VM IP address is 10.211.55.3&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Conceptually, our macOS machine is playing 2 roles in the VM network - both the gateway and as a participant in the network.  The SQL Server container running on our macOS host is available to the Windows VM at the IP address assigned to our host as a participant in the network.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://drewsktech.blob.core.windows.net/images/2024/January/network-vm.png&quot; width=&quot;400px&quot; alt=&quot;The IP address in question&quot; /&gt;
  &lt;figcaption&gt;The IP address in question&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Since the gateway address is x.x.x.1 and the VM is assigned x.x.x.3, I’m going to made an educated guess that my host machine is available at x.x.x.2.  All that IP address information I gathered by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ipconfig&lt;/code&gt; from the Windows VM.&lt;/p&gt;

&lt;h2 id=&quot;test-the-connection&quot;&gt;Test the connection&lt;/h2&gt;

&lt;p&gt;Using any SQL client application on the Windows VM will allow me to test the connectivity to the SQL container.  This moment is a good one to fire up &lt;a href=&quot;https://aka.ms/ssms&quot;&gt;SSMS&lt;/a&gt; in the VM.  I’ll use the address &lt;strong&gt;10.211.55.2&lt;/strong&gt; as my SQL server address.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://drewsktech.blob.core.windows.net/images/2024/January/ssms-test.png&quot; width=&quot;300px&quot; alt=&quot;Connecting in SSMS&quot; /&gt;
  &lt;figcaption&gt;Connecting in SSMS&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://drewsktech.blob.core.windows.net/images/2024/January/connected-ssms.png&quot; width=&quot;400px&quot; alt=&quot;Good news everyone&quot; /&gt;
  &lt;figcaption&gt;Good news everyone&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;If you encounter difficulties gaining connectivity, you may need to adjust firewall settings in macOS.  If your firewall is enabled, use the &lt;strong&gt;Options&lt;/strong&gt; button to allow specific connections.&lt;/p&gt;

&lt;h2 id=&quot;recap&quot;&gt;Recap&lt;/h2&gt;

&lt;p&gt;While there’s a lot of the development process that you can accomplish from just about any machine, there are still some specific instances where you need that Windows VM.  With the default shared network setting, your macOS host has an address assigned to it that you can determine and use to connect from the Windows VM to a container running on macOS.  In the setup on my machine, the Windows VM was assigned &lt;strong&gt;10.211.55.3&lt;/strong&gt; and the macOS host (and SQL Server container) was available at &lt;strong&gt;10.211.55.2&lt;/strong&gt;.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Using Python and Azure Functions to send data from Azure SQL Database</title>
   <link href="https://blog.drewsk.tech/blog/2023/02/26/using-python-and-azure-functions-to-send-data-from-azure-sql-database/"/>
   <updated>2023-02-26T00:00:00+00:00</updated>
   <id>https://blog.drewsk.tech/blog/2023/02/26/using-python-and-azure-functions-to-send-data-from-azure-sql-database</id>
   <content type="html">&lt;p&gt;When building applications on Azure SQL, one of the most flexible ways to send data from your database to other systems is to use Azure Functions. Azure Functions are serverless functions that can be triggered by a variety of events, including HTTP requests, timers, and &lt;a href=&quot;https://aka.ms/sqltrigger&quot;&gt;Azure SQL Database changes&lt;/a&gt;. In this article, we will discuss how to send data from an Azure SQL Database to an FTP server and API endpoints using Azure Functions.  The complete sample code for this article is available on &lt;a href=&quot;https://github.com/dzsquared/sqlbindings-python-datatransfer&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/generated/assets/images/python-sql-bindings-800-c2b8490e4.png&quot; srcset=&quot;/generated/assets/images/python-sql-bindings-400-c2b8490e4.png 400w, /generated/assets/images/python-sql-bindings-600-c2b8490e4.png 600w, /generated/assets/images/python-sql-bindings-800-c2b8490e4.png 800w, /generated/assets/images/python-sql-bindings-1000-c2b8490e4.png 1000w&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This post is syndicated from https://devblogs.microsoft.com/azure-sql/using-python-and-azure-functions-to-send-data-from-azure-sql-database/&lt;/p&gt;
&lt;/blockquote&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;get-data-from-azure-sql-database-in-azure-functions&quot;&gt;Get data from Azure SQL Database in Azure Functions&lt;/h2&gt;

&lt;p&gt;With &lt;a href=&quot;https://aka.ms/sqlbindings&quot;&gt;Azure SQL bindings for Azure Functions&lt;/a&gt; we can easily retrieve data from an Azure SQL Database in an Azure Function, leaving the boilerplate code of connecting to the database and executing queries to the Azure Functions runtime.  When our solution needs to operate on a schedule, such as every morning, we can use the &lt;a href=&quot;https://learn.microsoft.com/azure/azure-functions/functions-bindings-timer?tabs=in-process&amp;amp;pivots=programming-language-python&quot;&gt;timer trigger&lt;/a&gt; to start the Azure Function. Python Azure Functions are composed of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;function.json&lt;/code&gt; file and an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__init__.py&lt;/code&gt; file. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;function.json&lt;/code&gt; file is where we define the function trigger and input/output bindings and the Python code is based in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__init__.py&lt;/code&gt; file. Querying Azure SQL Database with an Azure Function is as simple as adding an input binding to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;function.json&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;products&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sql&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;in&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;commandText&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SELECT [ProductID],[Name],[ProductModel],[Description] FROM [SalesLT].[vProductAndDescription]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;commandType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;connectionStringSetting&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SqlConnectionString&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once we have the input binding defined, we can use the parameter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;products&lt;/code&gt; in our function code to access the data returned by the query. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;products&lt;/code&gt; parameter is a list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SqlRow&lt;/code&gt; objects, which are similar to Python dictionaries.&lt;/p&gt;

&lt;p&gt;The Azure SQL input bindings for Azure Functions can run any SQL query, including stored procedures.  The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commandText&lt;/code&gt; property is where we define the SQL query to run.  In the example above, we’re selecting four columns from the view &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SalesLT.vProductAndDescription&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connectionStringSetting&lt;/code&gt; property is where we define the name of the app setting that contains the connection string to the Azure SQL Database. &lt;a href=&quot;https://github.com/Azure/azure-functions-sql-extension/tree/main/samples/samples-python&quot;&gt;Additional examples&lt;/a&gt; are available which show using additional features, including parameters and executing SQL stored procedures.&lt;/p&gt;

&lt;p&gt;Throughout the sample we have several values in the Azure Functions application settings, including the Azure SQL connection string, the API endpoint URL, and the FTP server login information. Keeping this sort of sensitive information out of code is a best practice that you’ll want to follow.&lt;/p&gt;

&lt;h2 id=&quot;sending-data-to-an-api-endpoint&quot;&gt;Sending data to an API endpoint&lt;/h2&gt;

&lt;p&gt;To send data to an API endpoint, we will use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests&lt;/code&gt; library for it’s simplicity and the built-in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json&lt;/code&gt; library. With the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests&lt;/code&gt; library, we can easily send a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt; request to an API endpoint with the data we want to send.  The SQL input binding sends data as a list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SqlRow&lt;/code&gt; objects, which are similar to Python dictionaries. We can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;json&lt;/code&gt; library to serialize the data into a JSON string, which is the format that most APIs expect.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;everyDayAt5AM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TimerRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SqlRowList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Python timer trigger function started&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# convert the SQL data to JSON in memory
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;rows&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# get the API endpoint from app settings
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;api_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;API_URL&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# send the data to the API
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# check for 2xx status code
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;API response: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;API response: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In our Azure Function we check the API response status code to make sure the request was successful. If the status code is not in the 2xx range, we log an error. If the status code is in the 2xx range, we log a success message. By logging an error, we can monitor the Azure Functions logs to see if there are any issues with calling the API endpoint.&lt;/p&gt;

&lt;p&gt;That’s it! Those ~10 lines of Python are all we need to run a query against our Azure SQL Database and send that data to the endpoint we set in the Azure Functions app settings.&lt;/p&gt;

&lt;h2 id=&quot;sending-data-to-an-ftp-server&quot;&gt;Sending data to an FTP server&lt;/h2&gt;

&lt;p&gt;While we formatted the data as JSON to send to an API endpoint, we may want to send our data to an FTP server as a CSV file.  By using a package like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandas&lt;/code&gt;, we can quickly convert the data to a comma-separated format.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;everyDayAt5AM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TimerRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SqlRowList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Python HTTP trigger function processed a request.&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;products.txt&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;filesize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# convert the SQL data to comma separated text
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;product_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pandas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DataFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;products&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;product_csv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;product_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Python has a built-in library, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ftplib&lt;/code&gt;, that can interact with FTP servers.  After retrieving the FTP server information from the app settings, we can connect to the FTP server and upload the data.  Instead of writing the data to a local file before uploading to the FTP server, we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BytesIO&lt;/code&gt; class to handle the binary data to memory.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;n&quot;&gt;datatosend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;BytesIO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;utf-8&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# get FTP connection details from app settings
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;FTP_HOST&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;FTP_HOST&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;FTP_USER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;FTP_USER&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;FTP_PASS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;FTP_PASS&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# connect to the FTP server
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ftplib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;FTP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FTP_HOST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FTP_USER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FTP_PASS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;utf-8&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ftp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ftp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getwelcome&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# use FTP&apos;s STOR command to upload the data
&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;ftp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;storbinary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;STOR &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datatosend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;filesize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ftp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;ftp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;quit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;File &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; uploaded to FTP server. Size: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filesize&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; bytes&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h2&gt;

&lt;p&gt;With Azure Functions we have a low-overhead and flexible way to build application components and the Azure SQL bindings make it easy to retrieve data from Azure SQL Database.  In this article, we took a brief look at an approach to sending data from an Azure SQL Database to an FTP server and API endpoints with Python in Azure Functions.  If you’d like to dive into this sample further, the code is available on &lt;a href=&quot;https://github.com/dzsquared/sqlbindings-python-datatransfer&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>OMSCS Research Options</title>
   <link href="https://blog.drewsk.tech/blog/2023/01/03/omscs-research-options/"/>
   <updated>2023-01-03T00:00:00+00:00</updated>
   <id>https://blog.drewsk.tech/blog/2023/01/03/omscs-research-options</id>
   <content type="html">&lt;p&gt;The Georgia Tech online masters of science in computer science has been a groundbreaking endeavour to bring an MS CS program to students at a massive scale.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;In five years, the program has received over 25,000 applications and enrolled more than 10,000 students (including those who have graduated), all working their way toward the same Georgia Tech M.S. in Computer Science as their on-campus counterparts. - https://omscs.gatech.edu/explore-oms-cs&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/generated/assets/images/omscs-banner-800-e2a356067.png&quot; srcset=&quot;/generated/assets/images/omscs-banner-400-e2a356067.png 400w, /generated/assets/images/omscs-banner-600-e2a356067.png 600w, /generated/assets/images/omscs-banner-800-e2a356067.png 800w, /generated/assets/images/omscs-banner-1000-e2a356067.png 1000w&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Despite the use of online communities, legions of teaching assistants, and other techniques to expand the reach of a traditional masters of science program to thousands of students, there are still opportunities for students to be deeply engaged with university resources. One of these categories of opportunities is research and independent projects, which I was fortunate to experience in three ways during my time in the program.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;education-technology-foundations-cs6460&quot;&gt;Education Technology Foundations (CS6460)&lt;/h2&gt;

&lt;p&gt;Previous review comments &lt;a href=&quot;https://drewsk.tech/omcs-graduation-and-recap#heading-education-technology-foundations-cs6460&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’m starting with the EdTech course because the barrier to entry is low and the pathway to success is well guided. Registration for EdTech (CS6460) is open to OMSCS students without additional forms and even if you don’t have a project in mind before the course, the first few weeks of the course involve exploration of academic literature on the intersection of education and technology. If you do have a project in mind, the first part of the course will help you focus and refine your idea to best fit the remainder of the course.&lt;/p&gt;

&lt;p&gt;To be specific about your success in this course, you will agree on an outcome with a TA/mentor along with a planned approach when you decide on your topic. Along the way, adjustments can be made so its in your best interest to be honest with your mentor about challenges you are having or unforeseen barriers. In many cases your mentor will have experience in or adjacent to the area you’re working on and can provide helpful suggestions. Much of this is similar to working with a PI (principal investigator) in a research group except the EdTech course takes a more structured approach to the project plan than most research groups.&lt;/p&gt;

&lt;p&gt;After EdTech, you can optionally take your project further through a few avenues:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;participate in the student showcase, which is an online forum format of student presentations at the end of each semester&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;continue working on the project independently, which is especially applicable if you are building an open source component&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;approach a professor working in a related area and inquire if they’re willing to mentor you for continued work through CS8903 (see below), a number of EdTech projects will fall in &lt;a href=&quot;https://lucylabs.gatech.edu/david-joyner/&quot;&gt;Dr. Joyner’s areas of interest&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my opinion, unless you do 2-3x more work during the EdTech course than is required for the course, you are unlikely to have a project ready for publication in academic journals at the end of this class. However, if you continue the project past the course you may be on the way to publication. Regardless, EdTech can be a tremendously rewarding adventure into working on an individual or team project focused on an area of your interest.&lt;/p&gt;

&lt;h2 id=&quot;vertically-integrated-projects-vip&quot;&gt;Vertically Integrated Projects (VIP)&lt;/h2&gt;

&lt;p&gt;Previous review comments &lt;a href=&quot;https://drewsk.tech/omcs-graduation-and-recap#heading-vertically-integrated-project-big-data-and-quantum-mechanics&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;OMSCS students are eligible to apply for VIP teams, an email is usually sent once a semester to remind students but you can begin investigating options at any time. &lt;a href=&quot;https://www.vip.gatech.edu/online-mscs-students&quot;&gt;Applications&lt;/a&gt; are open around the time of phase 1 registration, so you need to be thinking ahead if at all possible.&lt;/p&gt;

&lt;p&gt;My &lt;em&gt;suggestion&lt;/em&gt; for participating in VIP is to not only complete the application to a team, but if you have related experience (professional or academic) or compelling interest in specific topic that you also communicate with the professor leading the team via email. Do enough background research into the work the team is doing to have engaging questions.&lt;/p&gt;

&lt;p&gt;An important component of VIP is the team component. There is a high likelihood that you will be collaborating with other students, undergraduate and/or graduate students. This makes VIP an excellent opportunity to learn from others but also to be a leader and help other students. OMSCS students can be highly desireable to VIP teams because we come to Georgia Tech with diverse backgrounds and experiences, exactly the kind of innovation that vertically integrated projects are seeking.&lt;/p&gt;

&lt;p&gt;My experience with VIP involved relating previous undergraduate and graduate research to the VIP project components, as well as areas I’m interested in learning more about. The VIP team I joined had a branch that required some development experience that I was able to work on where my previous experience was helpful in gaining context quickly and getting started.&lt;/p&gt;

&lt;h2 id=&quot;special-projects-cs8903&quot;&gt;Special Projects (CS8903)&lt;/h2&gt;

&lt;p&gt;Previous review comments &lt;a href=&quot;https://drewsk.tech/omcs-graduation-and-recap#heading-special-problems-cs8903&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Special projects does not change your OMSCS degree pathway from being coursework based, but it does create a significant opportunity to do research for credit (3 or 9 credits) that can be part of your coursework credits. These are credits that count towards your “free electives”, no matter what your specialization is. These statements are made based on the current content of https://omscs.gatech.edu/program-info/specializations, you will check with your academic advisor before pursuing.&lt;/p&gt;

&lt;h3 id=&quot;understanding-academic-research&quot;&gt;Understanding academic research&lt;/h3&gt;

&lt;p&gt;Academic faculty are highly motivated to conduct research, where there success is measured mostly by their publications but also by the success of their team (postdocs, graduate students, and extraordinary undergrads). The phrase “publish or perish” is invoked as a reminder that research &lt;em&gt;must go on&lt;/em&gt;. This is not to suggest that all academic faculty aren’t interested in your success as a student, but it’s really important to understand some of these dynamics if you want to successfully participate in academic research. First off - I’ll define your success as an OMSCS student in CS8903:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Making forward and novel progress on a project of sufficient difficulty that it makes for engaging conversation with people on a deep technical level&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Completing agreed upon checkpoints to complete the CS8903 credit hours. If you were a full time research student, you would be trying to meet expectations to continue your “employment” in the group&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Optionally, your work is published or included in a publication by the research group&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Research groups are generally structured horizontally into subgroups of people around projects under the PI (principal investigator, or faculty member). A research group around a PI can contain post-doctoral researchers (postdocs), graduate/PhD students, and undergraduate students. In larger groups, the more senior folks (postdocs) can take on a good deal of mentorship of student researchers. Newer faculty members will have smaller groups that are often more tight-knit in both the vertical and horizontal direction.&lt;/p&gt;

&lt;h3 id=&quot;getting-started-with-cs8903&quot;&gt;Getting Started with CS8903&lt;/h3&gt;

&lt;p&gt;The process for starting CS8903 is a bit more involved than the previous options, but the possibilities are nearly endless! There are &lt;a href=&quot;https://www.cc.gatech.edu/facts-and-rankings&quot;&gt;over 100 academic faculty in GT Computing&lt;/a&gt; and all of their research areas are potential content for your work for CS8903. To enroll in CS8903 and work on a faculty member’s research project, you will need to:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Educate yourself on their current work, including publications and opportunities for more advancement in that area. You may want to familiarize yourself with the GT Library to get access to some of the content.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Reach out (via email) to introduce yourself and/or complete any research interest forms they have linked on their website (not all faculty have these). Similarly to the introduction email I recommend for VIP (although now required), you’re trying to make a good impression - you’re smart, inquisitive, hard working, etc.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Ask the professor if they’re willing to supervise you for CS8903 where you’d work on their project &lt;em&gt;{fill in the blank here}&lt;/em&gt; based on your interest/experience/knowledge/unique charm. It’s ideal to have a virtual meeting with the professor mid-semester to discuss their work and brainstorm what you might work on. Look at the CS8903 permit form &lt;em&gt;before&lt;/em&gt; this meeting.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;If the professor agrees, complete and submit the &lt;a href=&quot;https://www.cc.gatech.edu/graduate-forms-procedures&quot;&gt;CS8903 permit form&lt;/a&gt;, including a statement of research.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Register for CS8903 during registration and setup regularly meetings with your advisor. If their research group does virtual or hybrid meetings, you may want to join those.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;The statement of research should be two or three pages and should include the following:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;
      &lt;p&gt;Problem Statement or Project Goals&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;Solution Proposal or Approach&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;Schedule of Work&lt;/p&gt;
    &lt;/li&gt;
    &lt;li&gt;
      &lt;p&gt;Expected Results or Outcome (Deliverables)&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;School of Computer Science Special Problems (CS 8903) Permit Form, https://www.cc.gatech.edu/graduate-forms-procedures&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I got started with CS8903 by digging further into the “Databases” &lt;a href=&quot;https://www.cc.gatech.edu/research-areas&quot;&gt;area of research&lt;/a&gt; and looking through all the faculty at Georgia Tech I could find that work with &lt;a href=&quot;https://db.cc.gatech.edu/&quot;&gt;databases&lt;/a&gt;. &lt;a href=&quot;https://faculty.cc.gatech.edu/~jarulraj/&quot;&gt;Prof. Arulraj&lt;/a&gt; is involved in a few projects, some newer and others carrying over from their original PhD work. Their research group is relatively small - which is a pro or con depending on a number of other factors. One of those projects was of significant interest to me, so I read no fewer than 10 papers of theirs or referenced by them before reaching out to ask about future opportunities.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;This article isn’t necessarily exhaustive and there may be other ways to conduct research in OMSCS, including reaching out to a professor similarly to as would be done for CS8903 but instead of under the premise of you doing research work “for free”. As the OMSCS program evolves over time, additional opportunities may become available for students. In my experience, all three of VIP, EdTech, and CS8903 options were readily available to customize the work done for OMSCS to fit my area of interest. Know your strengths and don’t be afraid to explore!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>OMSCS Graduation and Recap</title>
   <link href="https://blog.drewsk.tech/blog/2022/12/31/omscs-graduation-and-recap/"/>
   <updated>2022-12-31T00:00:00+00:00</updated>
   <id>https://blog.drewsk.tech/blog/2022/12/31/omscs-graduation-and-recap</id>
   <content type="html">&lt;p&gt;The online Masters of Computer Science program at Georgia Tech is completely online and designed for working, part-time students. I applied to OMSCS in 2015 when I was concerned about what I saw as a plateau ahead in my career. At the time I worked for a smaller company doing a little bit of everything in technology and development. With a MS in chemistry and a minor in computer science under my belt I was confident that I would be able to succeed in OMSCS and it would contribute to my part of my aspiration to be a better developer and work as software engineer in the future.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/generated/assets/images/omscs-banner-800-e2a356067.png&quot; srcset=&quot;/generated/assets/images/omscs-banner-400-e2a356067.png 400w, /generated/assets/images/omscs-banner-600-e2a356067.png 600w, /generated/assets/images/omscs-banner-800-e2a356067.png 800w, /generated/assets/images/omscs-banner-1000-e2a356067.png 1000w&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I received the decision notification email while driving from Missouri back home to Minnesota. It was unpleasantly cold and windy at a gas station in Iowa as I navigated the applicant portal from my iPhone. Also relatively unpleasant was the news that I had not been accepted to OMSCS.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;In December 2022 (this month), I graduated from OMSCS. In this post I’m going to talk just a bit about what I did between being rejected in 2015 and being admitted to OMSCS as well as review some pieces of OMSCS.&lt;/p&gt;

&lt;h2 id=&quot;pre-omscs&quot;&gt;Pre-OMSCS&lt;/h2&gt;

&lt;p&gt;I’m not going to speculate about the differences in my applications and the applicant pools between 2015 and 2020, but I do want to talk about how I was a different person going into OMSCS because of the additional experiences I did have in that time.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I developed a few skills for Amazon’s Echo/Alexa devices, and as a part of it started thinking more developer experiences and platform integration. I even took a day off once to go to an Alexa Dev event in Minneapolis “for fun”.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I got involved in professional organizations around the systems I used at work (SQL Server, Dynamics SL). With SQL Server I was able to get back to speaking/teaching and learned a whole lot from the great folks involved with SQL Saturday MN (PASSMN). With Dynamics SL I was able to learn more about the depths of the product in organizing content for conferences and eventually served on the Board of Directors.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I took a few &lt;a href=&quot;/2017/03/22/oh-hey-data-platform-mcse-2017/&quot;&gt;certification exams&lt;/a&gt; to solidify and validate technical knowledge.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I completed two professional certificate programs through Coursera focused on &lt;a href=&quot;https://www.coursera.org/specializations/product-management&quot;&gt;product management&lt;/a&gt; and &lt;a href=&quot;https://www.coursera.org/specializations/business-strategy&quot;&gt;business strategy&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I created a few extensions for Azure Data Studio.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To be honest, I wasn’t planning to re-apply for OMSCS. It was in early 2020 when I was looking to apply for new roles (landing in SQL experiences at Microsoft) that I decided to go after OMSCS again. I figured if one avenue didn’t work out, maybe the other would.&lt;/p&gt;

&lt;h2 id=&quot;omscs-review&quot;&gt;OMSCS Review&lt;/h2&gt;

&lt;p&gt;I worked on the &lt;a href=&quot;https://omscs.gatech.edu/specialization-computing-systems&quot;&gt;“Computing Systems” specialization&lt;/a&gt; and over 2.5 years completed the requirements for matriculation (2020-2022). COVID-19 restrictions were in full force during the early semesters - which had its benefits at times - but we had just moved to Washington and I had just started a new job. In later semesters all 3 of our dogs passed away. There are some colleges that have hybrid or online MS CS programs that still have significant synchronous or on-campus components, but Georgia Tech’s OMSCS is fully-centered in the online campus.&lt;/p&gt;

&lt;p&gt;Despite all the daily stresses and life events happening during OMSCS, my key to success was repeatedly setting aside everything else to focus on course content for hours every week. For some classes this might be in the range of 10 hours, for others it was 20+ hours. Given the demands of life, I am incredibly fortunate to have a supportive spouse who made a number of sacrifices over that span.&lt;/p&gt;

&lt;p&gt;There are some realities of a graduate program that can be jarring to the student, especially if they’re expecting more of a bachelors degree:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;the pace at which material is covered requires individual learning - know how you learn topics&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;the topics/subjects are provided, but the materials that you need to master it may not be explicitly provided - know how to look for books, papers, videos&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;the background of each student varies widely and some folks grasp topics faster than others - know how to leverage study groups or ask peers for advice&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;courses&quot;&gt;Courses&lt;/h3&gt;

&lt;h4 id=&quot;fall-2020&quot;&gt;Fall 2020&lt;/h4&gt;

&lt;p&gt;The common advice for the first semester is to take only a single class to allow you to adjust to graduate school and avoid becoming overwhelmed. I didn’t listen and added the VIP project course, but I also didn’t really read course reviews yet either.&lt;/p&gt;

&lt;h5 id=&quot;vertically-integrated-project-big-data-and-quantum-mechanics&quot;&gt;Vertically Integrated Project (Big Data and Quantum Mechanics):&lt;/h5&gt;

&lt;p&gt;⭐️⭐️⭐️⭐️_ (4/5 stars)&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://www.vip.gatech.edu/vip-vertically-integrated-projects-program&quot;&gt;Vertically Integrated Projects (VIP)&lt;/a&gt; program is a cross-functional research initiative at Georgia Tech where bachelors and masters students can contribute to larger projects alongside faculty and PhD students, where the increased innovation benefits the multidisciplinary projects and students earn course credit for their work. As a student, team assignment is a selective process, but for OMSCS students there are a good number of teams that are seeking CS expertise for their projects. I applied to the the &lt;a href=&quot;https://www.vip.gatech.edu/teams/vvi&quot;&gt;Medford group&lt;/a&gt; to work on their visualization software for ML-assisted DFT (&lt;a href=&quot;https://medford-group.github.io/ElectroLens/&quot;&gt;ElectroLens&lt;/a&gt;). The team was an ideal fit due to my original MS in chemistry where my thesis leveraged DFT and ElectroLens is based in Electron/JavaScript. I can’t recommend VIP highly enough, whether you have previous research experience or are looking for more guidance - you get great exposure to academic research through this program.&lt;/p&gt;

&lt;h5 id=&quot;database-systems-concepts-and-design-cs6400&quot;&gt;Database Systems Concepts and Design (CS6400):&lt;/h5&gt;

&lt;p&gt;⭐️____ (1/5 stars)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://omscs.gatech.edu/cs-6400-database-systems-concepts-and-design&quot;&gt;This course&lt;/a&gt; moved fairly slowly through entity-relationship diagrams, relational algebra/calculus, and report writing for a LAMP stack application. The preface for the textbook used literally recommends the chapters covered by this class for an introductory undergraduate course. I was sorely disappointed at the lack of coverage for index structures, query processing, transactions, or security (all included in the textbook for use at the graduate level). Frankly, I was so unimpressed with the databases course that if I hadn’t also done VIP that first semester I would have been tempted to stop the program and focus on promotions at work. I was concerned that every course would have such low quality of content. &lt;em&gt;I didn’t find this to be true, and am glad I continued on to better courses.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;group project (you can get team members who do nothing)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;rudimentary content all the way through&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;terribly worded exam questions&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;spring-2021&quot;&gt;Spring 2021&lt;/h4&gt;

&lt;p&gt;After carefully reading course reviews, I paired an easier course (Computer Networks) with a more time-consuming course (Intro to OS), both well-rated.&lt;/p&gt;

&lt;h5 id=&quot;computer-networks-cs6250&quot;&gt;Computer Networks (CS6250):&lt;/h5&gt;

&lt;p&gt;⭐️⭐️⭐️⭐️_ (4/5 stars)&lt;/p&gt;

&lt;p&gt;My systems administration background served me well in &lt;a href=&quot;https://omscs.gatech.edu/cs-6250-computer-networks&quot;&gt;this class&lt;/a&gt;, which had a nice balance between exams/quizzes/projects. The content looked at how some networking fundamentals work behind the scenes and the projects were often Python implementations of algorithms. Extra kudos to the instructional design for this class such that students could collaborate on creating test cases and/or unit test frameworks for the projects, which created additional learning opportunities.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;easy-to-moderate assignments&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;content chopped up into bite-size segments&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;graduate-intro-to-os-cs6200&quot;&gt;Graduate Intro to OS (CS6200):&lt;/h5&gt;

&lt;p&gt;⭐️⭐️⭐️⭐️⭐️ (5/5 stars)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://omscs.gatech.edu/cs-6200-introduction-operating-systems&quot;&gt;This course&lt;/a&gt; is legendary for its content, project time commitment, and instruction. The depth and pacing of the topics is not overwhelming, but studying for the exams with flash cards was necessary to make sure I was picking up the material sufficiently. On top of preparing for exams with the course videos and reading, this course’s projects (3 of them) are each about a 40 hour commitment (varying based on your experience with C, debugging, and acclimating to an existing codebase). I had at least 1 near-breakdown with each project, often at late hours of the night, as my brain stretched to grasp problem space and properly allocate memory. If I had to pick a favorite class from the program - this would have been it. After completing the course I wanted to take &lt;a href=&quot;https://omscs.gatech.edu/cs-6210-advanced-operating-systems&quot;&gt;advanced operating systems&lt;/a&gt; but ultimately didn’t fit it in.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;2 moderate difficulty exams that aren’t too highly weighted&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;3 difficult assignments with clear connection to course material&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;summer-2021&quot;&gt;Summer 2021&lt;/h4&gt;

&lt;p&gt;Some students opt to have the summer free from school and don’t take a class, especially since it’s an abbreviated semester. I opted for enrolling in classes that didn’t have enormous workloads during the summers.&lt;/p&gt;

&lt;h5 id=&quot;software-arch--design-cs6310&quot;&gt;Software Arch &amp;amp; Design (CS6310):&lt;/h5&gt;

&lt;p&gt;⭐️⭐️___ (2/5 stars)&lt;/p&gt;

&lt;p&gt;While &lt;a href=&quot;https://omscs.gatech.edu/cs-6310-software-architecture-design&quot;&gt;this course&lt;/a&gt; was full of busy work through in-module quizzes, it also offered a lot of supplementary/required readings. The most notorious is likely the &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/gangs-of-four-gof-design-patterns&quot;&gt;Gang of Four&lt;/a&gt; design patterns, but there many others including some solid nods to &lt;a href=&quot;https://en.wikipedia.org/wiki/Robert_C._Martin&quot;&gt;Uncle Bob&lt;/a&gt;. The course had about as many assignments on how to communicate the software architecture through UML/OCL as designing the architecture, which is valuable but there’s a depth of architecture design content available that is interesting to check out. The bulk of the grade for this course came from project assignments, which culminated in a group project component. Similarly to the first semester group project, there was a group member who ultimately shouldn’t have received any points because despite multiple people trying to bring them up to speed they were unable to build the project code much less contribute. As long as your group only has 1 or 2 people in this category (out of 4 or 5), you’ll be able to pull through and complete the work, but it is a risk. &lt;em&gt;I’m a huge supporter of the program’s high acceptance rate but I propose that it is reasonable that a subset of courses are approved for first-semester students that have only individual work to bottleneck students who need to ramp up.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;most valuable course content is in supplemental material&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;group project&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;“easy course” - highest grade in the program&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;fall-2021&quot;&gt;Fall 2021&lt;/h4&gt;

&lt;p&gt;Individually both of these courses are notable for content and instructional style, but they additionally share a significant amount of academic paper reading and writing. To spare myself the context switching, I paired the courses up for a semester where I could stay in the mindset of interaction design and writing.&lt;/p&gt;

&lt;h5 id=&quot;human-computer-interaction-cs6750&quot;&gt;Human-Computer Interaction (CS6750):&lt;/h5&gt;

&lt;p&gt;⭐️⭐️⭐️⭐️⭐️ (5/5 stars)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://omscs.gatech.edu/cs-6750-human-computer-interaction&quot;&gt;HCI&lt;/a&gt; is a “Dr. Joyner course” with 2 aligning sets of papers focused on the principles learned and applying those as methods towards a project idea. There’s a good amount of &lt;a href=&quot;https://omscs6750.gatech.edu/spring-2022/required-reading-list/&quot;&gt;engaging reading&lt;/a&gt;, and it certainly helps that I’m interested in the topic. I still some of those resources from time to time for work as a PM. As tempting as it was to use one of the SQL tools for the project, I ended up proposing improvement to the GPS navigation component of Apple CarPlay, and had a little fun along the way with &lt;a href=&quot;https://github.com/dzsquared/cs6750-randomimage&quot;&gt;user surveys&lt;/a&gt; and scraping product review APIs.&lt;/p&gt;

&lt;h5 id=&quot;education-technology-foundations-cs6460&quot;&gt;Education Technology Foundations (CS6460):&lt;/h5&gt;

&lt;p&gt;⭐️⭐️⭐️⭐️_ (4/5 stars)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://omscs.gatech.edu/cs-6460-educational-technology&quot;&gt;EdTech&lt;/a&gt; is another “Dr. Joyner course” and is &lt;em&gt;similarly&lt;/em&gt; structured to HCI but with a much larger independent project component. You are given a &lt;a href=&quot;https://omscs6460.gatech.edu/research-guide/&quot;&gt;general direction&lt;/a&gt; to investigate a gap in education where technology plays a role, and can conduct empirical research or implement improvements. My focus area, to no one’s surprise, was database systems. After scrubbing the literature on higher education for database systems, one of the chasms that forms is automated grading infrastructure for database assignments. I took a swing at the issue with &lt;a href=&quot;https://robertdroptablestudents.github.io/&quot;&gt;SQLGrader&lt;/a&gt;, designed to scale to courses such as the databases course I took in the first semester.&lt;/p&gt;

&lt;figure&gt;
  &lt;img src=&quot;https://robertdroptablestudents.github.io/assets/diagrams/arch.png&quot; alt=&quot;SQLGrader architecture overview&quot; /&gt;
  &lt;figcaption&gt;SQLGrader architecture overview&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I can’t say I continued working on SQLGrader despite there being many opportunities to improve it, as I had also started a project proposal for the next semester.&lt;/p&gt;

&lt;h4 id=&quot;spring-2022&quot;&gt;Spring 2022&lt;/h4&gt;

&lt;h5 id=&quot;special-problems-cs8903&quot;&gt;Special Problems (CS8903):&lt;/h5&gt;

&lt;p&gt;I was very fortunate that &lt;a href=&quot;https://faculty.cc.gatech.edu/~jarulraj/&quot;&gt;Prof. Joy Arulraj&lt;/a&gt; was receptive to my inquiry and project proposal, which took tiny step forward with his &lt;a href=&quot;https://github.com/jarulraj/sqlcheck&quot;&gt;SQLCheck tool&lt;/a&gt; for identifying antipatterns in SQL code. The second version of it was fresh off of feature in &lt;a href=&quot;http://vldb.org/pvldb/vol14/p2779-ghosh.pdf&quot;&gt;VLDB&lt;/a&gt;. The work I did was spit into 2 categories - the first was minor/quick improvements to the original (open source) SQLCheck and the second was to establish forward motion on the second generation of SQLCheck. This involved updating dependencies, hardening the container images with a few best practices, and establishing patterns for sharing compute resources with other projects in the DB research group (egress networks, SSL configuration, etc). The Special Problems course is designed to mix a thesis-driven Masters degree with a coursework-driven Masters degree such that the learner can dip their toes in or immerse further in a research area. I won’t give this a star rating because it is a phenomenal option for OMSCS students and Dr. Arulraj was a fantastic mentor, but I know my mental state was rapidly declining during 2022 and had I been engaged on this at a different time would have wished I got more out of it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I was very worn out from the program at this point and on top of it 2 of our dogs passed away during the spring semester. I’m very glad that I stepped back to taking 1 class at a time to avert total meltdown and to afford more time to spend with Ellie and Louie during their final days.&lt;/em&gt;&lt;/p&gt;

&lt;h4 id=&quot;summer-2022&quot;&gt;Summer 2022&lt;/h4&gt;

&lt;h5 id=&quot;software-analysis-and-testing-cs6340&quot;&gt;Software Analysis and Testing (CS6340):&lt;/h5&gt;

&lt;p&gt;⭐️⭐️⭐️__ (3/5 stars)&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://omscs.gatech.edu/cs-6340-software-analysis&quot;&gt;Software Analysis course&lt;/a&gt; covered automated testing/analysis concepts, for example looking at how compilers can detect issues like uninitialized variables in code. Many of the topics were applicable to the security of software (eg fuzz testing) but there’s a strong link to the importance of the tools that are used as software is written (or compiled). A good portion of the assignments/labs were leveraging &lt;a href=&quot;https://llvm.org/&quot;&gt;LLVM&lt;/a&gt; C++ APIs, however an attempt was made to use TypeScript/JavaScript in a lab to demonstrate type-checking and delta debugging was explored in Java. No complaints about the variety of languages, only complaint was that the course seemed like it attempted to hard to be “light” with short assignments and only a single exam. It’s not an exaggeration that a handful of the assignments were less than 60 minutes of work. This course could be significantly improved with more investment into the course assessments. Ok course and on the edge of being a good course.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;easy-to-moderate assignments, most of the difficulties coming from lab requiring an older version of LLVM&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;content chopped up into bite-size segments&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;machine-learning-and-data-science-tooling-seminar-cs8001&quot;&gt;Machine Learning and Data Science Tooling Seminar (CS8001):&lt;/h5&gt;

&lt;p&gt;The seminar concept was introduced just in 2022 and I was thrilled! These offerings are 1-credit hour options to interact with others and work through a breadth of light material on a topic, ideal for semesters where you have just a little extra time expected but not enough for a full course. I very much recommend trying out even just 1 during OMSCS.&lt;/p&gt;

&lt;h4 id=&quot;fall-2022&quot;&gt;Fall 2022&lt;/h4&gt;

&lt;h5 id=&quot;introduction-to-graduate-algorithms-cs6515&quot;&gt;Introduction to Graduate Algorithms (CS6515):&lt;/h5&gt;

&lt;p&gt;⭐️⭐️⭐️⭐️_ (4/5 stars)&lt;/p&gt;

&lt;p&gt;I’m torn on the rating for &lt;a href=&quot;https://omscs.gatech.edu/cs-6515-intro-graduate-algorithms&quot;&gt;Intro to Graduate Algorithms&lt;/a&gt; because it is a good course (4 stars) but it is a graduation requirement and structured differently than most other courses in the program, leading to unnecessary stress for the ~850 students each semester as they scratch and claw their way out (2 stars). The downfall of Georgia Tech’s popularity is that this course bottlenecks at the end of the program and is all but impossible to register for until your last 1, maybe 2 semesters. A number of students take it more than 1 time to receive a passing grade and I understand that there’s no real incentive to have students take this course earlier when they might quit the program instead of toughing it out in this course. It very much reminded me of teaching science for ITT Technical Institute to first-semester students and how the dean of students would come remind me, frequently, that I was grading too harshly and reducing the enrollment numbers for later courses. &lt;em&gt;Many other courses that were difficult to enroll in now have a reduced bottleneck so I hold out hope that in a few more years, graduate algorithms won’t be the last class every student takes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This course is notoriously difficult, almost a self-perpetuating cycle at this point, since a good portion of the difficulty comes from our responses to stress and inability to think under pressure. If you, like many other students in the program, are far removed from your last formal discussion of algorithms, learning how to even answer homework and exam questions will be difficult. That’s ok, you can handle that. You’re going to need to do a lot of practice problems anyway (dynamic programming, graph algorithms, divide and conquer, linear programming) to really grasp the material, and you’re going to do them until you don’t need your notes or any prompts to work out the answer completely. This class isn’t a leetcode prep course, and although it does introduce some concepts that can be helpful in that prep you will write &amp;lt; 200 lines of code for the class. Office hours (mini lectures) are provided weekly, which you can go to live or watch the recording. Study groups are recommended, and hopefully you are unafraid to leave/join study groups until you find one that works well for you. In your study group you should be able to ask questions and work through problems while your peers provide feedback (criticism, reinforcement, etc). When all is said and done, this class has three exams that are each 25% of your grade. The final exam is only an opportunity to replace one of those exam scores if you’d like to improve the grade you’re at, an opportunity I’ve heard isn’t available during the summer. I was sitting in the B range before the final exam and was quite happy to not need to take it.&lt;/p&gt;

&lt;h3 id=&quot;research&quot;&gt;Research&lt;/h3&gt;

&lt;p&gt;If you made it through my course reviews, you probably noted that I had a few opportunities to do research.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;#vertically-integrated-project-big-data-and-quantum-mechanics&quot;&gt;VIP&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;#education-technology-foundations-cs6460&quot;&gt;Education Technology&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;#special-problems-cs8903&quot;&gt;Special Problems&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite being a fully-online in both coursework and community program, Georgia Tech manages to offer multiple avenues for OMSCS students to do research - which is phenomenal. Whatever your aspirations are following OMSCS, there’s an opportunity to dive deeper into a subject that interests you if you’re ready to work independently.&lt;/p&gt;

&lt;h3 id=&quot;cost&quot;&gt;Cost&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Fall 2020: $1,381&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Spring 2021: $1,381&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Summer 2021: $841&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Fall 2021: $1,381&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Spring 2022: $841&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Summer 2022: $1,021 &lt;em&gt;(increased due to seminar enrollment)&lt;/em&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Fall 2022: $647 &lt;em&gt;(decreased for all OMSCS students with removal of institutional fee)&lt;/em&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Total: $7,493&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(One of my employer’s benefits is a generous amount of tuition reimbursement, which I used to cover the cost of OMSCS.)&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;I’m proud of having completed OMSCS and I’m really grateful for all the learning opportunities I had as a part of the process. I don’t have the same aspirations as I did during my first application to the program but I do know that even while in the program the knowledge I was picking up was immediately applicable to my work, to which I’ll credit some of my growth at work to OMSCS. It was a tough program and offers a lot of options for students to create their path that benefits them the most.&lt;/p&gt;

&lt;p&gt;Sometimes people would ask me “what’s it like working and doing school part time?” or “I’ve been thinking about doing a masters, how is it?” - to which my response is usually:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It’s really awful and painful, but I love it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thanks OMSCS, I loved it.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Azure Logic Apps and Form-Data HTTP Requests</title>
   <link href="https://blog.drewsk.tech/blog/2019/03/06/azure-logic-apps-post-form-data/"/>
   <updated>2019-03-06T00:00:00+00:00</updated>
   <id>https://blog.drewsk.tech/blog/2019/03/06/azure-logic-apps-post-form-data</id>
   <content type="html">&lt;p&gt;The nuts and bolts of this post is about sending an HTTP POST request in an Azure Logic App that utilizes the multipart/form-data content type. I don’t run into it often, but when I do, I’m sure glad I figured out how to do more than application/json request bodies in Logic Apps. The use case I came across this week for multipart/form-data body was for the &lt;a href=&quot;https://documentation.mailgun.com/en/latest/api-sending.html#sending&quot;&gt;Mailgun&lt;/a&gt; API.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;the-mailgun-example&quot;&gt;The Mailgun example&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--user&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;api:YOUR_API_KEY&apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Excited User &amp;lt;mailgun@YOUR_DOMAIN_NAME&amp;gt;&apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;YOU@YOUR_DOMAIN_NAME &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;bar@example.com &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Hello&apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Testing some Mailgun awesomeness!&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above example is directly from the Mailgun documentation, but we need to translate it into an Azure Logic APPS HTTP request.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;This is a POST request&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The URL is https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;You need a header for “Content-Type” with a value that sets form-data and a boundary. This example value would work just fine “multipart/form-data; boundary=—-WebKitFormBoundary7MA4YWxkTrZu0gW”&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We’ll cover the body text area in a moment.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Authentication is basic, username is api and the password is your private api key from the mailgun dashboard.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a POST request - The URL is https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages - You need a header for “Content-Type” with a value that sets form-data and a boundary. This example value would work just fine “multipart/form-data; boundary=—-WebKitFormBoundary7MA4YWxkTrZu0gW” - We’ll cover the body text area in a moment. - Authentication is basic, username is api and the password is your private api key from the mailgun dashboard.&lt;/p&gt;

&lt;h3 id=&quot;request-body&quot;&gt;Request Body&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;------WebKitFormBoundary7MA4YWxkTrZu0gW&lt;/span&gt;
Content-Disposition: form-data&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;from&quot;&lt;/span&gt;

Excited User &amp;amp;lt&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;mailgun@YOUR_DOMAIN_NAME&amp;gt;
&lt;span class=&quot;nt&quot;&gt;------WebKitFormBoundary7MA4YWxkTrZu0gW&lt;/span&gt;
Content-Disposition: form-data&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;to&quot;&lt;/span&gt;

YOU@YOUR_DOMAIN_NAME
&lt;span class=&quot;nt&quot;&gt;------WebKitFormBoundary7MA4YWxkTrZu0gW&lt;/span&gt;
Content-Disposition: form-data&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;subject&quot;&lt;/span&gt;

Hello
&lt;span class=&quot;nt&quot;&gt;------WebKitFormBoundary7MA4YWxkTrZu0gW&lt;/span&gt;
Content-Disposition: form-data&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;text&quot;&lt;/span&gt;

Testing some Mailgun awesomeness!
&lt;span class=&quot;nt&quot;&gt;------WebKitFormBoundary7MA4YWxkTrZu0gW--&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The body content is comprised from sets of 4 rows following the form field boundary value:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;label for the form field, such as Content-Disposition: form-data; name=”from”&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;empty line&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;form field value&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;the form field boundary value as stated in the Content-Type header value&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;through-postman&quot;&gt;Through Postman&lt;/h2&gt;

&lt;p&gt;If you’re working with HTTP requests I highly recommend a tool such as Postman to test, save, and modify your endpoints. In the instance of creating a form-data request for Azure Logic apps, the “Code” functionality in Postman can save you a bit of time.&lt;/p&gt;

&lt;p&gt;You would start by building the POST request you would like to make in Postman, including the form values in the Body tab.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drewsktech.blob.core.windows.net/images/wp-content/uploads/2019/03/formdata_postman.png align=&amp;quot;center&amp;quot;&quot; alt=&quot;Completed request in Postman&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After the information is entered into Postman, click the small “Code” action in the upper right, which opens a popup with the request build in a selection of languages. We want to switch &lt;em&gt;HTTP&lt;/em&gt; to &lt;em&gt;Java OK HTTP&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drewsktech.blob.core.windows.net/images/wp-content/uploads/2019/03/postman_code.png align=&amp;quot;center&amp;quot;&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Two sections of code are important to grab from here for use in Azure Logic apps. The first is the highlighted request body code, highlighted. The second is the header value for content-type on line 8, which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW&quot;&lt;/code&gt; in this example.&lt;/p&gt;

&lt;p&gt;Back in the Azure Logic apps UI, switch from &lt;em&gt;Designer&lt;/em&gt; to &lt;em&gt;Code view&lt;/em&gt;. You will paste the request body in for the &lt;em&gt;body&lt;/em&gt; value here.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://drewsktech.blob.core.windows.net/images/wp-content/uploads/2019/03/codeview.png align=&amp;quot;center&amp;quot;&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You will need to complete the rest of the request values, including the Content-Type header, for your request to be complete.&lt;/p&gt;

&lt;h2 id=&quot;azure-logic-app-http-requests&quot;&gt;Azure Logic App HTTP Requests&lt;/h2&gt;

&lt;p&gt;I love leveraging Logic Apps to simplify the interaction from the endpoint application and reduce potential change points when a 3rd party has API changes. Most frequently I’m hitting a POST endpoint that accepts a JSON body, but I still occassionally see form-data. By creating a boundary string in the header and setting the body up with the 4-line sets (label, blank, value, boundary), you can send these requests in Azure Logic Apps.&lt;/p&gt;
</content>
 </entry>
 

</feed>
