CI Artifact Creation For SQL Server

This article is part of my SQL Server CI/CD Series. In it I will be talking about creating artifacts related to SQL Server in your the CI part of the build chain.

What is an artifact?

First lets define an artifact within the narrow scope of Continuous Integration (CI) an artefact is anything created during a build that is retained for download. Typically  this is a zip file or other package of the compiled application and a html rendering of test reports.

Artifacts we will produce

We are going to produce the following artifacts:

  • A backup of the database taken after all tests are run
  • A dacpac of the schema
  • Output of some queries we run through SQLCMD

We can produce more artifacts like SQL Unit test reports, database diagrams, or even scripting out the entire database. We will do that in a future article. For now, we will stick to these three artifacts.

Why do we want to produce these artifacts?

Just because you can’ doesn’t mean you should. You don’t want to create artifacts unless they will be used. So what is the point of producing each artifact.

Database Backup

In a CI process that involves SQL Server, you’re probably going to create a database. You are then going to modify it as part of your testing. In the end you are going to end up with a “gently used” database. You will likely have all your schema setup, any “static” data, and some test data. You’ll have some statistics, and fragmentation as well. You might want to restore the backup to your laptop and inspect all this stuff. Perhaps manually verify that your assertions in your tests are covering what you think they should.

You also might want a copy of the database that someone can restore to run a local copy of their code, or reports against.

Finally, if any of your CD pipelines are designed to create a fresh database, your going to need this artifact in that CD pipeline. As a matter of fact you might end up creating two backups. One at the point where your database is in a clean “initial state,” and another after all your automated testing has been run.

Dacpac

I hold the strong opinion that the best way to deploy schema changes to a database is a dacpac. You’re welcome to disagree with me, and like everything else in SQL Server, the answer it depends. However, My advice in these articles will always be around the assumption that the dacpac is the gold standard for automated deployments of schema changes.

That being said, if you prefer to use scripts or another tool to manage schema changes, then you are wasting storage and build server CPU creating a dacpac.

Sqlcmd output

Sometime you just want to generate a quick and dirty report in T-SQL to look at. Maybe there is information you want to be aware of, but don’t necessarily want to fail a build, like index fragmentation after the tests are run. Perhaps someone just wants a report to look at after a test run.

Regardless, sometimes sqlcmd –i inputfile.sql –o outputfile.txt is good enough to get you home.

How do we create these artifacts in AppVeyor?

In our introduction I stated we would use AppVeyor as our CI tool, because its free for open source projects, and because its simplicity makes it easy to explicitly show how we accomplish tasks. The steps are all segments of appveypr.yml.

First of all, we need to start SQL Server. We use the services section of appveyor.yml to specify what services to start. I happen to use SQL Server 2016 because that’s the version of SQL Server that Always Encrypted was introduced on.

services:
- mssql2016

Now, every step we use to get the database to its final step is a subject for another blog. I’ll just simply this.

  1. We use environment variables to configure the connection information.
  2. We create the database with sqlcmd.
  3. Other stuff happens to the database.

The relevant environment variables in our case are:

environment:
  SQL_SERVER_INSTANCE: (local)\SQL2016
  SQL_SERVER_USER: AlwaysEncryptedOwner
  SQL_SERVER_PASSWORD: 7aO!z@xUu!4r6EvD#D&l$sz6&h^rhxL6fzAHMpnOga@LO*WdsEdpfh4^Egtl
  SQL_SERVER_DATABASE: AlwaysEncryptedSample
  SQL_SERVER_BACKUP_FILE: $(SQL_SERVER_DATABASE).bak
  SQL_SERVER_DACPAC: $(SQL_SERVER_DATABASE).dacpac
  SQL_SERVER_VERIFICATION_LOG: $(SQL_SERVER_DATABASE).verification.log

Our creation script is run here:

before_build:
- cmd: sqlcmd -S "%SQL_SERVER_INSTANCE%" -i .\appveyor\init.sql

The script itself is:

CREATE DATABASE [$(SQL_SERVER_DATABASE)];
GO
CREATE LOGIN $(SQL_SERVER_USER) WITH PASSWORD = '$(SQL_SERVER_PASSWORD)';
GO
-- Needed for dacpac creation
-- GRANT VIEW ANY DEFINITION TO $(SQL_SERVER_USER);
-- GO
USE [$(SQL_SERVER_DATABASE)];
GO
CREATE USER $(SQL_SERVER_USER);
ALTER ROLE  db_owner  
       ADD MEMBER $(SQL_SERVER_USER); 
GO

It goes without saying that all SQL Server services on AppVeyor build machines use mixed mode authentication and the user that executes AppVeyor steps is a system administrator on all SQL Server instances.

Creating the sql login/user is superfluous, and a step I might remove from that script.

Other stuff happening in the database is the part I’m going to brush over. We now have an empty database that we can perform artifact creating operations on.

Creating the Artifacts

As of the time I wrote this article all my sql artifacts are created in after_build. This is before unit tests are run. None of my tests or any subsequent steps currently modify the database. At this point these artifacts operate on a “fresh” database.  Moving them to after_test would give me artifacts that have the test result data in them, if any.

after_build:
- cmd: sqlcmd -S "%SQL_SERVER_INSTANCE%" -d %SQL_SERVER_DATABASE% -W -i .\appveyor\schema_verification.sql -o %SQL_SERVER_VERIFICATION_LOG%
- cmd: sqlcmd -S "%SQL_SERVER_INSTANCE%" -Q "BACKUP DATABASE [$(SQL_SERVER_DATABASE)] TO DISK='$(SQL_SERVER_BACKUP_FILE)' WITH FORMAT, COMPRESSION, STATS=10;"
- ps: Move-Item -Path (Join-Path $env:SQL_SERVER_BACKUP_DIRECTORY "$($env:SQL_SERVER_BACKUP_FILE)") -Destination $env:APPVEYOR_BUILD_FOLDER
- cmd: sqlpackage.exe /Action:Extract /TargetFile:"%SQL_SERVER_DACPAC%" /SourceServerName:"%SQL_SERVER_INSTANCE%" /SourceDatabaseName:%SQL_SERVER_DATABASE%

Right now schema_verification.sql looks like this:

PRINT 'Tables in database:'
SELECT 
        QUOTENAME(s.name) + '.' + QUOTENAME(t.name) AS [Table Name] 
    FROM 
        sys.tables t INNER JOIN sys.schemas s
            ON s.schema_id = t.schema_id;

I was actually using this to manually verify that the tables were being created. This is a stop gap for actual automated tests of the SQL Schema.

Also a note on the Move-Item. AppVeyor only lets you create artifacts from files inside the folder where the git repo is checked out. I probably could just specify the full path of the backup file in the CREATE BACKUP T-SQL statement. I’m pretty sure the image is setup so there would be no security issues.

Finally, a note on the environment variables used by the Move-Item invocation. SQL_SERVER_BACKUP_DIRECTORY is set from data in the registry so I need powershell to set it dynamically like so.

install:
- ps: $env:SQL_SERVER_BACKUP_DIRECTORY=(Get-ItemProperty "HKLM:\Software\Microsoft\Microsoft SQL Server\MSSQL13.SQL2016\MSSqlServer").BackupDirectory

And APPVEYOR_BUILD_FOLDER is provided to us already. That’s the folder where the git repo is checked out.

So now that we have created these artifacts, we must publish them. This is where the artifacts section of the yaml files comes in.

artifacts:
  - path: $(SQL_SERVER_BACKUP_FILE)
    name: Database backup
    type: file
  - path: $(SQL_SERVER_DACPAC)
    name: DACPAC
    type: file
  - path: $(SQL_SERVER_VERIFICATION_LOG)
    name: Verifiction Query Results
    type: file
  - path: $(MSBUILD_LOG_FILE)
    name: MSBuild BInary Log
    type: file

So as you can see, any file we can make via powershell, the command line or any of the tooling the AppVeyor images provides us can become an artifact. Artifacts are by no means limited to SQL Server, that’s simply the focus of this blog series.

Introducing the SQL CI/CD series

Articles in the series

About the series

Perhaps the hardest part of your software stack to integrate into a CI/CD (Continuous Integration/Continuous Deploy) chain is your database objects. Now Microsoft has made some great strides in this, and its actually not that bad at all if you happen to use all the Microsoft best practices, and have a simple use case, ie:

  • You’re using Agile, Scrum, Kanban, etc. on a closed source project with a single production instance.
  • A private git repo with a .NET Core MVC App and an OLTP database preferably hosted on GitHub or Azure DevOps (formerly Visual Studio Team Services online).
  • Schema managed by a SSDT Project in Visual Studio
  • Any ORMs are not generating schema, and developers are manually handling creating models (C# classes that map to the RDBMS tables).
  • All code coverage and unit tests reports will be viewed in Visual Studio or from the Azure DevOps Dashboard.
  • Build Pipeline Creating a DACPAC in Azure DevOps
  • Release Pipeline does a full DB backup and Deploys a DACPAC in Azure DevOps
  • Release pipeline failure plan is to restore DB backup and rerun last good release
  • Every merged PR gets deployed to dev
  • Manual gates for Production Deploys
  • Database deployed to Azure SQL DB PaaS

If you stay within these lines, and you don’t have any schema or data changes that require a specific migration strategy that present a problem for SSDT, then things are easy.

However, reality doesn’t always match this ideal, especially if the DBAs were brought in after the fact. Even if  DBAs were brought in you could be in a shop that doesn’t bleed Microsoft Blue. Or you could simple be overruled on something. Heck, you could even be a DBA that doesn’t want to do some of the things on that list.

Perhaps your reality, for whatever reason looks like the following:

  • This is an open source project.
  • This project is a demo for training, conference talks, user group talks, and blog articles, so you need many different artefacts produced.
  • Your Git repo is a complex mono repo or contains submodule with projects in different languages
  • You’re main web app is .NET 4.6.1 with MVC 5 and EF 6.2
  • You’re using a third party CI/CD tool such as AppVeyor
  • You’re using other third party services in your CI/CD chain such as codecov.io.
  • Part of your schema is from a Code First EF6 application
  • Your build pipelines is creating several artifacts
    • Website deploy zip file
    • Nuget Packages for some of the DLLs
    • Noget Packages of Powershell Modules
    • SQL DACPAC
    • SQL Schema Generation Script
    • Backup of database Unit test were run against.
    • Code Coverage and unit test reports in HTML format and junit
    • Merges to master trigger a github release and all generated artifacts are sent there.

I happen to have such a project, the AlwaysEncrypted sample application I wrote around the release of SQL Server 2016 that I’ve recently dusted off to improve my DevOps skills. I’ve decided to document some of the steps in my journey along the way as a series of articles on this blog.

About the sample project

While I’m no fan of code first ORMs, I wanted this app to demonstrate AlwaysEncrypted “just worked” even if your app wasn’t built around it. Instead of Azure DevOps, I went with AppVeyor because its free, and since its more simple than Azure DevOps build pipelines, and configured by YAML, I can illustrate the how and why better than “here is a GUI step in Azure DevOps”

A simple Azure SQL DB to Azure SQL DB External Table How To

I’m working on an app for a client with some interesting requirements. The app has its on Azure SQL DB thats ultimately for staging data, but is part of a project with a Data Warehouse. We needed some office location data from the data warehouse to populate a drop down in the app and I decided to try the EXTERNAL DATA SOURCE feature.

External data sources seem similar in concept to linked tables but are much different in practice. They let you connect to hadoop data, azure blob storage, do elastic tables, and expose a table in one database to another. The last one is the least sexy, but the one I want. It’s also the option with the least attention in books online and blogs. I’m here to fix that.

Defining our problem

Of course these are not the real names of my clients servers, dbs and tables.

The data warehouse is on a server called mywarehouse.database.windows.net. The database name is myWarehouse. It has a table called dbo.dimFacilities. We want to expose a subset of that tables columns to a database called loadingDb on myapp.database.windows.net.

Doing the work

loadingDb is going to need to authenticate to myWarehouse and perform some selects. Therefore we need to create a user in myWarehouse. for loadingDb and assign it to the db_datareader role.

CREATE USER app_dw_reader WITH PASSWORD = '************';
GO
EXEC sp_addrolemember @rolename = 'db_datareader', @membername = 'app_dw_reader';
GO

It’s probably better to create a custom role with just the selects on the table we want, but that’s an exercise for the reader. You are not limited to contained users here. You can also use AzureAD users or users associated with a login.

Now its time to turn our attention to loadingDb. The first two objects we will create are a MASTER KEY and a SCOPED CREDENTIAL.

CREATE MASTER KEY ENCRYPTION BY PASSWORD = '**************';
GO
CREATE DATABASE SCOPED CREDENTIAL dw_reader WITH IDENTITY = 'app_dw_reader', SECRET = '********';
GO

Note the MASTER KEY password is a distinct password the SCOPED CREDENTIAL password must match the users password. The master key is used to encrypt the scoped credential. When you change the password for app_dw_reader, you need to use ALTER DATABASE SCOPED CREDENTIAL to update the secret.

Now that we have that the next object is the external data source This somewhat analogous to adding a linked server entry.

CREATE EXTERNAL DATA SOURCE data_warehouse
WITH (
TYPE = RDBMS,
LOCATION = 'mywarehouse.database.windows.net',
DATABASE_NAME = 'myWarehouse',
CREDENTIAL = dw_reader
);
GO

Note this command will succeed even if the credential is invalid as long as the credential object exists. You don’t get an error until you try to query the linked table. Luckily its a useful error.

Now we need to define the table and the columns. You can define just a subset of the columns, but column definitions must match exactly or you get an error on select. Luckily, the errors returned if the select failed list the column in question.

CREATE EXTERNAL TABLE [dbo].[dimFacility]
(
[facilityId] int NOT NULL,
[facilityName] NVARCHAR(100) NULL,
[country] NVARCHAR(64) NULL,
)
WITH (
DATA_SOURCE = dev_data_warehouse
);
GO

Tangential note on column definitions. If you are using sys.columns.max_length to get the length of NVARCHAR columns divide that by two because that is length in bytes. The azure portal query editor doesn’t show column lengths, so I decided to use DMVs.

Now if everything worked out well you should be able to do a SELECT * FROM dbo.dimFacility; in loadingDb and get you data. Congratulations!

Listing Your Azure AD users In Excel with Power Query

This article is part of a series on Using Power Query For Excel as an Azure AD Dashboard. As its the first article besides the introduction it assumes zero knowledge of Power Query. The screenshots in this article were taking using Excel 2016 on Windows 10 with an Office 365 Business subscription. If something doesn’t work quite right on another version of Excel, leave a note in the comments.

Goals of this article

We are going to accomplish the following:

  • Install Power Query if we are running an older version of Excel
  • Identify our domain’s tenant id via both the Azure Portal and AzureRM
  • Create a basic OData query
  • Hide some superfluous columns with the Advanced Query Editor. I pick columns that are superfluous to me

Things we will do in future articles

We are going to do a lot in future articles, but these are things you might be chomping at the bit to do.

  • Render some lists as semi-colon delimited.
  • Add some new columns to tell us quickly if a row is a Regular user,
    B2B user, Local B2C or Federated B2C.
    This is one thig the Azure Portal
    is not completely clear about, and the inspiration for me learning Power
    Query.
  • Store configuration data such as Tenant Id in the spreadsheet. In this example we are going to hard code everything. Next article I will (while assuming very little Excel knowledge) show you how to configure this.
  • Break out sub tables such as the group membership. While some of the data in the feed gets rendered as Lists, and can easily be transformed into semi-colon rendered lists, Tables are a little more difficult.
  • Allow editing functionality. We might use VBA-Web to do that in a future article.

Installing Power Query

If you have Office 2016 Power Query is built in. If you have office 2010 or 2013, you can download the add-in right here.

Identifying Our TenantId

Every Azure AD Domain has a Guid called a TenantId associated with it. On that note, everything about Azure has a Guid or two associated with it. I’m going to show you two ways to get that tenanted.

The Azure portal

Log into https://portal.azure.com. Using the leftmost navigation column or the Search button up top navigate to Azure Ad.

Navigating to Azure Active Directory in the Azure Portal

From the second most left column select Properties. You should no see amongst other things the tenants Tenant Id, which is labeled as Directory Id. There is a little clipboard icon to let you copy it to the clipboard.

Selecting the Tenant Id

Via Powershell

You should have the AzureRM module already installed. Open PowerShell and type Connect-AzureRM. Enter your azure credentials in the resulting dialog and then copy the tenanted from the resulting output. If you have multiple tenants, you probably know which tenanted you want.

Now place that in notepad, or a cell in a blank excel document. You’re going to need it.

Creating The OData Query

Create a blank workbook. Navigate to the Data tab of the Excel menu. Select Get Data | From Other Sources | From OData Feed.

Excel Add Data From OData Feed

In the resulting dialog enter the URL https://graph.windows.net/TENANT_ID/users?api-version=1.6, replacing TENANT_ID with the guid we found before.

At this point you will need to authenticate. My credentials are cached and I don’t know how to invalidate them. You will need to login with an Organization account.

You will then see a preview dialog like so.

Power Query Preview Dialog

You can now hit Load to load to a new worksheet in the workbook, or if you want more control select Load To. Note that you can create a Connection Only, if you only want to use this data in other Power Queries. You can also add this query to the Data Model (something I don’t fully understand at the time of writing this).

Power Query Load To Options

After you Hit Load or Load To and Ok, you will finally see your data in an Excel Table. Congratulations, you’ve loaded Azure Ad data into excel!

Hiding Columns

You don’t want to hide the worksheet columns. You want to edit the Power Query and remove columns there.  When you created the Query the Queries and Connections panel should have appeared on the right. Right click on the query you just created and hit edit.

Excel Queries and Connections Panel

Now right click on any column and click on Advanced Editor.

Power Query Removing a Column

In the resulting editor you should see the following:

let
    Source = OData.Feed("https://graph.windows.net/TENANT_ID/users", null, [Implementation="2.0", Query=[#"api-version"="1.6"]]),
    #"Removed Columns" = Table.RemoveColumns(Source,{"objectId"})
in
    #"Removed Columns"

Lets note a few things here.

  • Most Excel generated Power Query involved a series of variables defined under let that refine the previous variable
  • The first variable is called source.
  • Variables can have spaces in their names but use the Notation #”Variable Name”
  • The in clause declares the rendered results.
  • C/C++ style variables apply here
    • // Everything till the end of line
    • /* Multiline comment */

Now lets remove a bunch of columns I personally don’t care about. If you wanted to use Azure AD to make a contact information sheet, this is the exact opposite of the columns you want to omit.

Using the Advanced Editor change the query like so.

let
    Source = OData.Feed("https://graph.windows.net/TENANT_ID/users", null, [Implementation="2.0", Query=[#"api-version"="1.6"]]),
    RemovedColumns = Table.RemoveColumns(Source,{
        "createdObjects",
        "department",
        "employeeId",
        "facsimileTelephoneNumber",
        "legalAgeGroupClassification",
        "jobTitle",
        "telephoneNumber",
        "mobile",
        "givenName",
        "physicalDeliveryOfficeName",
        "consentProvidedForMinor",
        "sipProxyAddress",
        "streetAddress",
        "city",
        "state",
        "country",
        "postalCode",
        "ageGroup",
        "companyName",
        "preferredLanguage",
        "manager",
        "directReports",
        /* I assume there is some kind of inheritance reason that things could be memvbers of a User, but I don't want to explore that here */
        "members",
        "transitiveMembers",
        "owners",
        "ownedObjects",
        "appRoleAssignments",
        "licenseDetails",
        "oauth2PermissionGrants",
        "ownedDevices",
        "registeredDevices"
    })
in
    RemovedColumns

I didn’t feel the space in the variable name added anything so I change it to RemovColumns. That’s probably my developer bias.

And now we have the beginings of a useful report.

image

Using Power Query For Excel as an Azure AD Dashboard

Azure Active Directory is a great piece of technology that can do several things around authentication. However, the UI provided by portal.azure.com is lacking, and documentation can be a little unwieldly. This article serves as the first in a series of that will show you how to use Microsoft Power Query (and maybe some VBA) to create a better dashboard for user and application management for Azure AD.

Technologies utilized

In this series we will be using the following technologies.

  • Microsoft Excel to host the dashboard. Power BI would also work, but I don’t know Power BI (at least yet).
  • Microsoft Power Query to query the graph APIs to get the data we need and present it into Excel Tables
  • Windows Azure Graph will be the OData REST API that Power Query will use to

I have the beginnings of a dashboard that solves some problems I’ve encountered during about a year of Azure Development. I still have to figure out a few things to get the dashboard I really want. While I’m pretty good with Excel, I am very new to Power Query. As an advanced Polygot programmer that is new to the Power Query language I struggle to create code elegant reusable code in Power Query due to my ignorance of its syntax. I’ll be learning along with you.  This list might grow.

Getting the Dashboard

I’m actually not sure yet. The main artifact of this project will be an Excel Spreadsheet with Power Queries for the Azure Graph APIs, and maybe some VBA to push the data. I’d like to store the Power Query code as text files in GitHub. Solutions for storing VBA as text in version control exist (e.g. RubberDuck VBA).

Also, I need to figure out a reliable way to strip all the data from my tenant from the spreadsheet.

I’m going to also look into distributing this via OneDrive or Office365 SharePoint. I’m actually not sure if I can make public links there.

Why not Microsoft Graph?

The short answer is at the time I started using Azure AD, many things I wanted to do didn’t work yet in the Microsoft Graph. One of the main use cases I will talk about here is B2C users, which as of 2018-08-31 is still not supported by the Microsoft Graph. Support does say “Coming soon.”

The long answer is as follows Certain operations require you to use one of two graph APIs, the old Azure Graph (https://graph.windows.net) or the new Microsoft Graph (https://graph.microsot.com). To clarify that statement, while both graph APIs expose more functionality than the azure portal (http://portal.azure.com), some operations only work with one endpoint. In theory eventually the Microsoft Graph will be a complete superset of the Azure Graph. That hasn’t happened yet.

Articles in this Series

On dishwashers and development.

Around 1976 a great thing happened in the house I would come to purchase in June of 2013. A KitchenAid KDI-18 was installed. Those with a penchant for vintage appliances believe this is one of the greatest home dishwashers ever made. Their spouses generally believe they are too loud.

Mine gave up the ghost recently. I could have saved it, but it was the dishwasher or the marriage. I went with a Bosch SHXN8U55UC. It’s whisper quiet. Like all German Dishwashers it has no heating element, but is engineered to produce smaller water droplets that evaporate quicker. Its gets the water hotter than the more expensive Mieles. It has a sanitize mode so I don’t have to take an extra step to sterilize my infant’s bottles and pacifiers. It uses very little water and is incredibly energy efficient. It’s like comparing a 1976 Cadillac DeVille to a modern BMW 7 series.

The cost of this efficiency is time. A cycle takes 2:09 and if you add sanitizing it climbs to 2:30. There is a save time function that takes an hour and change as well as a 30 minute express function. In addition to all this, there is the temptation to overload the dishwasher and pack things too close together. If you are not careful something might slip below the tray or be too high. Then a wash arm won’t rotate. All of these things means your dishes don’t get as clean.

This is very similar to software development. Putting too many things on the schedule or cutting back the delivery time frame might work in the short term. However, it means a more fragile result. Once I notice one dish is still dirty when unloading the dishwasher, It slows down my whole unloading process. I lost confidence in the operation of the machine. If I have to hand wash a dish it uses a lot more energy and a lot more of my time. Time I could better spend doing a DIY project around the house. I’m not a great handyman, but I’m the most talented handyman and tradesman on staff at my house. When I’m taken off my home improvement duties to deal with dishes that are not perfectly clean, it costs the house in materially significant ways.

So folks in conclusion, run your dishwasher on their full cycles unless its a very light load. Don’t let the dishes touch each other. Observe how well your dishwasher performs. Think about this next time you want an estimate cut and a schedule moved left.

In defense of the VBA developer

Yesterday I did what I was put on this earth to do, get into an argument on the internet. I  defended VBA.

https://twitter.com/flippbreezy/status/747856641903628288

. . .

 

Well with humility I present those thoughts here. However, before I present those thoughts I need to make three confessions:

  1. I use and have contributed to a tool that modernized the ALT+F11 VBA Editor.
  2. I have used and contributed to a tool that lets you make REST calls from VBA.
  3. I have written code to facilitate low level debugging of VBA with Sysinternals Process Monitor (procmon.exe).

Just to be clear, I am by no means a full time VBA programmer, I’ve simply dabbled extensively. In terms of hours logged or LoC its quite clear this is not a major endeavor of mine.

Ok my biases are reported. Full speed ahead with the apologia.

Sometimes Excel is the right tool for the job. There are many reasons for this.

Sometimes its a technical reason. For example, QuantLib is a really great quantitative library with binding for many languages such as C, C++ and Python. QuantLibXL is the excel binding for them. Excel is great at doing math and accounting. Quantlib just takes that to the next level with quantitative finance. It’s an obvious organic extension.

Sometimes you just need a little VBA. For example, in my sysadmin days I’d often be given spreadsheets of servers for audits. A little VBA would allow me to do reverse DNS lookups on the IP address columns in these spreadsheets. Whatever sin there is in adding VBA was a small one, and it would not be practical to not use Excel for the task because it wasn’t my decision.

Sometimes Excel is just being used to prototype a front end. Sometimes a developer that doesn’t want to write a web front end would rather use VBA-Web to write a quick API for an analyst for some REST services. This allows the analyst to own the front end and extend it, while the developer can do what they are good at. I’ve been in this situation. XL allowed me to be more of a back end programmer and not a designer.

Finally sometimes you’re in a silo in a large org with locked down desktops and cannot get Visual Studio, Python or R installed on your machine. However, you have office and VBA isn’t locked down. That decision to not support all those development environments is probably the right decision for the overall health of the org. A few smart people at the top lock everything down and they can scale out support issues with low salaried minimally trained help desk workers. Despite this your silo’s managers and developers allowing VBA abominations to grow beyond what makes sense within your silo. Its a crappy situation overall, but you have a silo doing the best they can to serve their larger organization even if the org cannot properly facilitate them. I’ve never personally been in this situation. However, I’ve interviewed at investment banks.. A former boss of mine worked at an investment bank that used VBA extenively. I’ve had many a beer with sysadmins at investment banks. Its quite clear to me why both sides do what they do.

Is excel often not the right tool for the job? Absolutely! It has been described as the second best tool for any task. The actual truth is it the tool people have and know how to use that gets shoehorned into a lot of tasks that could better be solved by other tools. Often these tasks have deadlines that makes excel the right tool at the moment pragmatically speaking. Excel is kind of like WD-40. WD-40 is a great solvent, that has some lubricating properties. However, its usually not the lubrication you want to use. Its great in a pinch if you don’t have machine oil. Just realize it’s going to quickly evaporate away unlike machine oil.

On Today is a Good Day to Die

Yesterday was SQL Saturday 517 in Philadelphia. As usual Joey D’Antoni(blog|twitter) and his team put on a great event. I look forward to coming back next year if I am selected to speak. On my way there, at the Molly Pitcher Rest stop where I put money on my E-ZPass, I sent a not particularly unusual tweet for me:

Now if you follow me on twitter you’ve probably seen me tweet that as well as extended forms that usually is a subset of the following.

Do something important today . Find your purpose in your utility to others. This way if today ends up being your last do on this earth, your last thought will be. “Today is a good day to die.”

However, someone that didn’t follow me, but was looking at the #sqlsat517 hashtag saw this and asked someone (who happened to be someone I know) if I was a terrorist. Unfortunately, I arrived late and this person left early. Therefore I could not personally assure this person I was not a terrorist. I’ve reached out through twitter, but this person does not tweet every day. Maybe that means he still actually got an email with my @reply. Regardless, I will do everything in my power to personally address his concerns if he still has them.

That being said, perhaps an extended commentary on the thoughts behind these tweets are in order.

First of all for the record, they are 100% inspired by Worf on Star Trek TNG and DS9. However, it’s evolved into so much more beyond that.

Second, lets talk about “Do something that matters.” Someone once complained that they felt it was depressing to read this because they felt their job was mainly “making rich white guys richer.” Well the point is to do something important, but for you to determine what is important. Maybe the main thing you can say about your job is it pays a lot better than anything else you’re qualified to do. Well maybe you get to mentor someone there, who will leave and help cure cancer. Maybe some customer  will indeed achieve greatness indirectly through your company. Maybe you can go home an volunteer in a soup kitchen. Maybe you can convince your boss to give back some contributions to an open source project you use at work. Maybe you will go home and change your kids diaper and play blocks with them. I’m not here to judge what you consider important. I’m not even sure what I consider important. I’m just saying, try to do something important every day. You can decide what is important, and if you need to alter the direction of your life to do more important things.

Third, lets talk about “find your purpose in your utility to others.” I think this is something we as a society are moving away from. People used to sign letters “I remain in your service.” Important people like George Washington who usually wrote to people of a lower station than him.  We’ve fallen away from formal letters, but we still send emails that are formal enough to warrant such a heady conclusion. Anyone that knows me knows I am a very independent person. However, I crave being useful. When a blog post by Thomas LaRock lead me to read The Broken American Male by Rabbi Shmuley Boteach, I rejected his premise that being a “human doing” is inherently a bad thing. Being a human doing that is overly concerned about money is certainly a bad thing. However, a well balanced human doing that does stuff for their family, their job, and for extra curricular organizations is a desirable thing IMnsHO.

Fourth, lets take a stab “Today is a good day to die.” I’ve never wanted to die. I’ve seen shrinks on occasion for moderate depression, but I’ve never been suicidal. The highest form of life I killed was a pig, which I ate part of. I’ve also slaughtered two chickens and a goat, and recommend all meat eaters do slaughter a mammal at least once in their life. There might be one or two people on this earth I wouldn’t mind seeing in the obituary section of the newspaper (no, not you), but I’d make all normal attempts to save their life if I saw them bleeding or unconscious. So in addition to not wanting to die, I’d rather avoid killing someone except to save the lives of others.

All that being said, we’re all going to die someday, and other than my religious beliefs about the afterlife, the best I can say about the matter is death is the one thing people actually think its a good thing to procrastinate over. As someone who files extensions on April 15th so I can file my taxes October 15th, I consider myself an authority on people opinions of procrastination. Despite our best efforts at procrastination, we will all die eventually. I have a young family now. It would suck for my wife and kids if I died. However, I’ve taken out life insurance to mitigate some of the more tangible losses. When life ends for me it will go on for everyone else. That day might be today, tomorrow or decades from now. Whenever it is, I do know that I’ve lived a fuller life and seen more things than most people that have ever lived on this planet, so it would feel selfish to say I’ve had anything but a good life. Therefore, is it that much a stretch to say today is a good day to die?

Is bro culture just more comfortable for males?

Zeorthly, and aside. This post as not inspired at all by this posts or the jerkwad that left Jen such crass feedback. Timing is coincidental.

I decided to meditate. Based on previous advice from @sqlanodyne I went with this guide, with the additional caveat that I’d do a lotus pose during the meditation no matter the cost or consequence. Over-committing made me the programmer I am today, perhaps in time it can make me someone that can meditate with my legs crossed. On the second day I had some lingering pain around my knees and I decided to take some yoga classes. I paid $50 for a month of unlimited classes at a hot yoga studio that had the virtue of being the closest yoga place to my house. I went to a class. I sweated. No full or half lotus was done. This is not about that. This is about me being in a yoga class with only one other male.

It went generally well. I felt generally comfortable. No one seemed uncomfortable by my presence. The instructor was not the best yoga instructor for n00bs, but I sat in the back, watched everyone, and made do with the situation. Years of being a socially inept shy person makes me comfortable in a socially uncomfortable situation. I have an odd feeling of familiarity when quietly observing others in an unfamiliar situation and following along.

Then I though about what would be a more comfortable situation for me. Something not comfortably uncomfortable, but comfortably comfortable. The answer is a weight room. A bunch of guys bigger than me, teasing me, but spotting me and pushing me to be stronger. Not at all an inclusive situation. A literal testosterone filled situation. However, I have experiences dating back to high school in a weight room. I have a place and a rank. That rank may be Omega, but in the Yoga studio I am Omega with an asterisk. I am other. In the weight room I belong. Someone will spot me. Someone will invite me into their groups rotation.

So what’s the conclusion? Mindfulness and relativity. The platinum rule. Treat others like they want to be treated, not how you want to be treated, because other people don’t want what you want. Maybe your comfortable with your brogrammer behavior. Maybe someone less white, less male and less twenty-something is not. Build an org where some feel like the omegas, but none feel like they have asterisks.

The dark side of learning from our mistakes

IT is largely unlicensed and unregulated. People have different feelings about this. As a college drop out with no certs besides a ZCE in PHP 5, I’m a big fan of the situation. One positive of this, is that most of the good people in IT tend to be self motivated to learn. We tend to learn from our mistakes, and learn from bad situations. However, the downside of this is mastery is often achieved through bad situations.

I’ve often said I’m paid as well as I am and I’m as skilled as I am because I was often underpaid and overworked in my youth. If I didn’t take jobs that didn’t pay enough that required more than I was capable of, I would not be forced to become the developer I am today. This is objectively true for me, and possibly for many others.

So what’s the dark side of this? Cheap bosses and clients got what they paid for. I got decent pay during my faux-apprenticeships. Decent companies and jobs existed for when I was ready for them. That is all kosher in my book.

The dark side is we suck at true mentorship and apprenticeship. Stackoverflow and other sites let us curate the tribal knowledge of how do do specific things. Training exists and great trainers abound. I’d like to think I’m a decent trainer. However, its hard to get an opportunity for one on one mentorship. I wouldn’t know how to teach a green developer in a holistic manner. I could just give them tasks and let them drown because it worked for me. However, I think there is a better way. I just don’t know what that way is.