Thursday, September 21, 2023

How to Disable Either Classic or YAML Pipelines in Azure DevOps (ADO)

If you have pipelines in Azure DevOps that you don't want to trigger automatically either because they are legacy and remain for reference, not currently being used, or just need to temporarily disable, there are different ways to accomplish this based on the type of pipeline.

Classic Pipelines

  1. Select the Classic Pipeline and press the 'Edit' button

  2. Select 'Triggers'

  3. Uncheck the box for 'Enable Continuous Integration'

  4. Save the Pipeline

YAML Pipelines

  1. Select the Classic Pipeline and press the 'Edit' button

  2. Select the ellipsis (3 dots) in the top-right hand corner, and select 'Settings'

  3. Select the 'Disabled' radio button, and press 'Save'

  4. The pipeline is confirmed disabled via the label next to the name

Wednesday, September 20, 2023

Leveraging IntelliSense for Azure DevOps YAML Pipelines in Visual Studio Code

If you're building out YAML pipelines for Azure DevOps (ADO) and don't wish to hand roll them in the online editor provided, you can instead build them in Visual Studio Code. The 1st thing you'll be hunting down though is IntelliSense and auto-complete assistance for your .yml pipeline code. Out-of-the-box, VSC doesn't have this support other than generic YAML language services so you'll want an extension. There is an extension for this and name of it is simply, 'Azure Pipelines' which can be found in the Marketplace

The issue is the Microsoft extension for the ADO pipelines is poorly rated at 2 out of 5 stars which made me wonder if it wasn't worth using. The good news is that rating isn't in reflection for a completely orphaned product as the latest commit was within a week of this writing. The main critique which I verified is after installation it just doesn't seem to do anything. However, with a single-step I got the primary functionality working as desired and have the IntelliSense and auto-complete features I needed.

The key is to manually switch the 'Language Mode' setting in the bottom-right hand corner to the proper value for the specific code file being created (or use the keyboard shortcut Ctrl + K M). Initially it will show 'YAML' which has been auto-detected, and in this mode none of the extension characteristics work. 

Click the dropdown and you'll get a selection of languages that can be set. Select, 'Azure Pipelines.'

Now the .yml file you're working on will have IntelliSense features, auto-complete, and hinting leveraging the installed extension which is helpful in creating pipelines.

Friday, July 28, 2023

Reading Environment Variables, AppSettings, LocalSettings, and User Secrets Seamlessly Across Environments in ASP.NET

One of the biggest challenges we face in modern cloud solutions is making it so sensitive configuration data can be read seamlessly across all environments from the local dev environment all the way to production. This isn't really a new issue, but one that has multiple ways to accomplish some easier than others.

For a sample use case, we might have a database connection string with sensitive information that needs to work seamlessly in code for local, Dev, QA, Stage, Prod, etc. without a lot of special hooks or handling in code. One way to handle this in the cloud regardless of deployment resource or setup is to expose a single key name with unique value per-environment. This can be configured via scripting in deployment pipelines per environment to take care of the DevOps side of this equation. However in code we want this single-read of a key to work in all environments. This is where in .NET the Configuration provider offered in Microsoft.Extensions.Configuration works so well.

Let's say the following environment variable has been configured across Dev, QA, Stage, and Prod environments with respective values already:
CosmosDbConnectionString | AccountEndpoint=https://cosmos-acct-{env};AccountKey=abc123...
For local development we have a couple options as well to configure this value:
  1. Create your own environment variable on your local OS with the identical key name and whatever value you choose
  2. Add the identical key and again whatever value you require as a 'User Secret' within Visual Studio or VS Code (VS Code requires an extension download to work with secrets)
The above 2 methods are preferred as they do not add to files that get committed to source control. While you can add the key/value pairs to appSettings.json or launchSettings.json (as env variables), these are not advised as it breaks the requirement that we should not be committing secrets to source control. Note - you could also write a PowerShell script to be added to the solution that developers could run initially to pull environment variable key names from the cloud modifying with a local value for an added bonus to be even more automated.

So moving back to our .NET code, we would like to have a single line that uses 'CosmosDbConnectionString' that will work for all environments.

To get started, in current versions of ASP.NET, this single line of code in program.cs sets us up out of the box:

The absolutely fantastic feature of this black-box is that it will automagically handle the hierarchy of running through multiple different configuration sources including environmental variables! This single line of code (after injecting the IConfiguration configuration service) is all that's needed:

Here is the official documentation on this from Microsoft:
Using the default configuration, the EnvironmentVariablesConfigurationProvider loads configuration from environment variable key-value pairs after reading appsettings.json, appsettings.Environment.json, and Secret manager. Therefore, key values read from the environment override values read from appsettings.json, appsettings.Environment.json, and Secret manager.
This is great, because now we can add our connection string as a local secret (or local environment variable) and have it read at runtime during debugging as required, but once deployed the configured environment variables per-environment will supersede anything from appSettings, secrets, etc. and continue to work with the same code.

This is also the case for non-sensitive data stored in appSettings.json locally vs configuration in a deployed environment like Azure. Any configuration settings in Azure per-environment using the same key name will supersede locally configured values. This use case is more well-known but worth mentioning as it's an anti-pattern to have different environment configs managed at the source control level; it should be managed in deployed environments via DevOps.

A last point to review is naming conventions when creating environment variables so they work platform agnostic. Per the same MSFT docs linked above:
The : delimiter doesn't work with environment variable hierarchical keys on all platforms. For example, the : delimiter is not supported by Bash. The double underscore (__), which is supported on all platforms, automatically replaces any : delimiters in environment variables.
Therefore if you need a hierarchical key consider using the following naming structure which will work on Windows and Linux:
This can be read regardless of deployed environment in code with the following line:

Thanks to the .NET configuration provider having all of this included functionality, we can work across 1..n environments using a multitude of configuration options, yet being able to garner that data at runtime with a single line of code!

Thursday, June 8, 2023

How to Reorder the Profiles in the Windows Command Prompt / Terminal

The Windows Command Prompt / Terminal tool has gotten more robust over the years, and makes using a tool like ConEmu (that I used for years), less of a pressing need. That's because the tabbed profiles are available, and makes selecting new tabs for different profile types (Bash, PowerShell, Azure Cloud Shell) a breeze. However there isn't a configured setting option for reordering for those of us that like to organize this sort of thing (i.e. making the ones I use the most at the top of the list). There is a feature enhancement on GitHub (Profile reordering for Settings UI), but for now there's a trivial way to go ahead and modify it today. Here's a picture of the profiles I have setup currently:
I want to reorder these, and the easiest way to do this is to open the 'Settings' from the dropdown of profiles above and select, 'Open JSON File' from the bottom left-hand corner of the dialog:
Now it's simply an act of rearranging the index in the array collection within the settings file. The index location in the array will dicate the order of the profiles in the command prompt window:
Once the changes are complete, save the file, restart the command prompt, and you'll see the new order of the profiles!

Monday, February 13, 2023

The State of JavaScript and Modern Web Client Development

Here is a link to a white paper I wrote named, 'The State of JavaScript and Modern Web Client Development.' If you need help navigating the waters at a high-level in the modern web landscape, make sure to check out the article.

Monday, November 7, 2022

Overloaded Methods in TypeScript

If you've ever worked with a language like C# or Java you're probably often using overloaded method signatures to provide callers with an opportunity to have a similar outcome by passing a different number or type of parameters. However in TypeScript which is just a superset of JavaScript and adhering to all things JS under the covers, method overloading doesn't work the same with identical method names and parameter signature because there are no types to differentiate between. If you have (2) identical methods in JS being called the latter defined method on the prototype will be called, and the former ignored.

TypeScript has the benefit of type definitions at build time so method overloading is possible... kind of. If the goal is to have intellisense to see multiple definitions of the same overloaded method, we can certainly achieve that. If the goal is to have multiple definitions of the same method name with different parameter signatures and separate, different implementations, this out of the box is not possible and won't be like traditionally static typed, structured languages like C#. Regardless let's see how overloading does work in the vanilla form (I'll hint at conditional types at the end) using TypeScript and you can decide if you can leverage for your benefit.

The recipe for overloaded methods in TypeScript is that you can create 1...n method signatures, but only have a single implemented method representing any of the possible call combinations. Let's look at a code sample:

Above we have the overloaded method named start, that represents the potential to provide the different procedures to start an engine, based on the various engine types. Note the (5) overloaded method signatures, but only a single implemented method. The reason for this is the overloaded behavior and differentiation of methods is a design/build time only feature available due to the fact we are using TypeScript. In fact if you look at the transpiled JavaScript the only method shown is the single implemented method:

If you do try and use implementations on more than 1 method with the same name, TypeScript will warn you with a, "Duplicate function implementation" warning.

Looping back to our original goal using the properly implemented code, as the method caller if we want to see at design time a list of the various signatures, we have indeed accomplished that goal as you may scroll through and see the multiple, overloaded definitions:

However this comes at a bit of a sloppy cost for that single implemented method. In order to make this work the method signature must encapsulate all potential values that could be sent to satisfy the TypeScript compiler. This usually equates to using a Union type in the method signature to account for all possible types. The next hurdle is because overloaded methods are really a fa├žade, you must manually pick apart what's sent and reverse engineer what you received at runtime. This usually equates to type guards, if statement, switch statements, or some combination to sniff out what you received, so you can proceed forward. All of this logic is that code above within our implemented method to determine what exactly we received.

It's even trickier to determine what's sent if you have (2) identical method signatures that are only differentiated by variable name like our 1st two methods below. This is not advisable even though it does work:

The long and the short of method overloading in TypeScript is that it is possible, but with a few caveats that may not make it sensible. I think if you only have (2) different method signatures, that are easily discernable at runtime in the implemented code, then this might make sense. However as the signature list expands, the logic to differentiate the potential values sent can get unwieldly. 

Lastly another potential option may be to use conditional types in the method signatures which rely on generics to sort out the types based on what the caller is sending. This could reduce the need for the implemented method to contain all the logic to sort out which values it was sent as it will be know already. However in this post I wanted to strictly do a 1:1 look at the concept of overloading as it may be known from other languages, and how it can be accomplished in TypeScript.

Wednesday, October 26, 2022

How to View Deployed Files for a Web App Using the Azure Portal

When debugging a web app and tracking down problems, especially newly created and deployed apps that aren't working, sometimes all that's needed is to look and see what's deployed to help determine the issue. This is trivial to do using the App Service Editor available from the Azure Portal.

Once logged into the portal, navigate to the website or specific deployment slot of the app where you'd like to browse the files. Viewing the menu of options on the left-hand side, select App Service Editor:

This will redirect to a location in a new browser tab which will show the folder structure and physical files present. Within the utility there are other useful functions available along the left-hand side in addition to the logging output to help diagnose and have insight into your deployed site.

This feature is still in 'Preview' mode so expect some changes over time to the utility, possibly being renamed, and on occasion being down. As noted you can always FTP into the directory as well as a secondary option.