If you've been following this blog, you will remember that I covered how to manage your secrets during development a few weeks back. And it all started with keeping your secrets out of source control as the first precaution.

Now we also want to have a safer process when hosting web applications on Azure, and thus avoid leaking secrets. So let's say I have a typical scenario as shown in the image below where I need to set an important secret.

Application settings insecure value

Storing secrets in the App Settings is already a step in the right direction, but the above password is available to any person in the team who has access to the app configuration. And with bigger teams, it already starts to sound like a bad idea. Which it absolutely is.

The first suggestion you will be given in this case is to move the secret password into an Azure keyvault. And this is where most developers start scratching their head, because to access the keyvault you need a password. And if you have the keyvault password, you can access the hidden secrets within. So we would not have solved the problem in this way, only just made it more annoying.

The answer lies in using managed identities. Let's try it!

Create a Key Vault inside your favourite resource group by clicking the +Add button.

Add new resource button

Search for and choose the Key Vault option.

Key vault item in dropdown

Then click the Create button.

create key vault button

Next, make sure to give your keyvault a nice name that can help you associate the keyvault with its purpose in the future. The region should be in the same region as the application that is going to access the keyvault. What with latency and all. Finally, the pricing tier gives you an option to store the secrets inside the keyvault using Hardware Security Modules. It's a tradeoff between additional peace of mind and costs.

New keyvault configuration

Feel free to take a look at the other settings, but for this tutorial you can jump to Review + create, and then confirm by clicking the Create button. You may paricularly want to take a look at the extra security given by the networking option, but that's something for another post.

Review and create keyvault

While the vault is being created, let's go back to the web application settings. Just below the app configuration menu, we have the Identity menu. We want to create a System Assigned Managed Identity, so flip the switch to the on position and save.

Enable managed identities

You will get a prompt to confirm this. So what are we really doing here? In simple terms, we are creating an identity for the web application. An identity is the Active Directory account that the web app will use to log into the keyvault. And the best part is, the Azure platform will handle the account for us. No one will ever know the login details for the web app to access the key vault. It will just work.

Managed identity confirmation prompt

So let's do this and click Yes. The keyvault deployment should be ready by now, so let's go there and click on the access policies menu.

Access policies menu

You should now have a + Add Access Policy button. Go on, click it!

This is where need to give the app acess to get secrets from the keyvault.

Add access policy with get secret permission

When it comes to selecting the principal, click on the link that says 'None selected', then search for and select the identity we have just created in the previous step. When searching you can use the app name, or the managed identity's object id.

Selecting app principal

That's it. All we need is those two values and we are good to go.

Access policy configuration

Once added, we should be able to confirm it looks good right away.

Confirming that policy was added

We can move the secret password to the keyvault now. Click on the Secrets menu and + Generate/import.

Create the secret inside keyvault

Then enter the secret.

Generating a secret

Once created, you can click on the secret and copy the secret identifier in the details page by clicking on the copy button.

copy keyvault secret identifier

Back in the application settings, replace the previous value with @Microsoft.KeyVault(SecretUri=<secret identifier>). Use the secret identifier you just copied instead of <secret identifier>.

referencing keyvault from application setting

That's it. Once you save your changes, the web app will be restarted. If everything was successful you should see a green checkmark next to the app setting.

key vault reference successful

If there are any issues, check the error to learn what went wrong. It's very easy to forget saving at any particular point in the process, so if this is the case go back and check that everything is there.

Once the app setting is accepted, also make sure that you check whichever secret you moved into the keyvault is still working as expected or your visitors will be disappointed.

I was recently asked to start sending an email at the end of every sprint, listing the details of our official stable builds. The problem is that I don’t like writing emails, so I wrote a release pipeline PowerShell script instead.

I will show you how to get all the relevant information for the release summary, but will not go into the details of sending the email itself here. I’ll leave that as an exercise for you, perhaps you can try to use SendGrid for that and tell me how it goes.

Our starting point is that I have two products, each of them with an existing build pipeline. For the purpose of this tutorial, I just created two blank builds for two fictional apps. However, I want you to notice the different version number for each app in the screenshot below.

We start from 2 existing pipelines

Those version numbers are the ones we need to publish to our stakeholders at the end of each sprint cycle. Let’s head over to the release pipelines now and click on the ‘new pipeline’ button.

Start with an empty job:

Start with an empty job

Which should produce the following result:

New, empty release pipeline

Now we need to add the two artifacts that are produced by the builds we saw earlier. Click the +Add button:

Choosing the artifact source

Select any one of them, and more configuration options will be shown.

Artifact setup

Most of the time you would want to always use the latest build, but you have various options for selecting an earlier or a specific version. Note that you will be prompted to confirm the build version anyway when you actually run the release pipeline at the end of this tutorial. For the source alias, I chose a simpler name like BlueYonder, since long names and spaces have caused issues for me in the past.

We can now click the Add button, and proceed to do the same procedure for the other app. Once ready, we should end up with two artifacts.

2 artifacts successfully added

Add this point, just save and create a release. I know we are not ready yet, but trust me just go ahead and create it.

Save, create release buttons

We can now see that the release is created.

Status shows that the release is created

Soon it will be queued and run.

Click on the release link as shown above, and then on the next screen, click the Logs button to view the logs. Some mouse-hovering ninja tricks are required to see the Logs button.

Release pipeline result

On the next page, select the job initialisation log:

Choosing to view the job initialisation log

This will show us, amongst other things, all the environment variables that were initialised for this job. Here I can find the variables that are holding the build information that I need. Look, there’s even my name down there. Isn’t that lovely! I can now sign the summary with the name of the person who triggered this release. So make a note of all the variables you need, or just keep it in another tab for further reference.

Find all of the variable values

We are now ready for the next step and add some code. Go back to the release configuration page and click on the link that says ‘1 job, 0 task’. This will open the Tasks configuration window.

Adding a new agent job

Note that here I already changed the stage name, which means I am at peace with my inner self and can proceed to add the agent job with the click of the + button. This will allow me to select the job type from the panel on the right.

PowerShell extension for Azure DevOps pipelines

Type ‘powershell’ to filter the results, and click on the Add button. You actually need to hover on the PowerShell job for the button to magically appear.

Configure the script as an inline script. Then enter the code shown below. Make sure to change <Your_Azure_DevOps_Organisation> and <Your_Azure_DevOps_Project> with your own organisation and project ids accordingly.

Adding the PowerShell inline script

Write-Host "Hi,"
Write-Host "Today we are pleased to announce the latest stable release of our awesome apps!"
Write-Host "WingTip Toys Collectors App v$(Release.Artifacts.Wingtiptoys.BuildNumber): https://dev.azure.com/<Your_Azure_DevOps_Organisation>/<Your_Azure_DevOps_Project>/_build/results?buildId=$(Release.Artifacts.Wingtiptoys.BuildId)&view=artifacts&type=publishedArtifacts."
Write-Host "Blue Yonder Flight App v$(Release.Artifacts.BlueYonder.BuildNumber): https://dev.azure.com/<Your_Azure_DevOps_Organisation>/<Your_Azure_DevOps_Project>/_build/results?buildId=$(Release.Artifacts.BlueYonder.BuildId)&view=artifacts&type=publishedArtifacts."
Write-Host "Best regards,"
Write-Host "$(Release.RequestedFor)"

Save and create another release, and this time we should see the summary with all the juicy details.

The output form the PowerShell script shows the required information

So here we are. I just showed you how we can obtain all the required information, which means we can go to the next step and send it as an email. However, this post is already quite long, and as I said at the beginning will leave it as an exercise for you.

Header image adapted from GalleryBritto / CC BY-SA (https://creativecommons.org/licenses/by-sa/4.0)

When setting up a new .NET Core project, one must be careful about where to store secret information, such as passwords and connection strings with sensitive data. Nowadays, it is very easy to start a new project and share its source code with the whole world, and likewise, it is very easy to share your secret information if you are not careful. It is of utmost importance that secrets are kept that way.

Azure makes it easy to store secrets on production websites and keeping them safe, but it’s mostly in their own development environment where most beginners struggle. If the software that you are building needs a password to connect to a database, the tooling will lead you down the path to storing it directly in the application settings. But then it would end up in the source code. Storing secrets inside source code has to be avoided at all costs!

The next best thing, and equally cheap at the price of free is to store your secrets in your own personal folder and retrieve them from there when needed. Luckily Visual Studio can do this out of the box. One important fact to be aware of before we continue is that this method does not encrypt the secret data in any way. It just stores it in your own personal folder, which is definitely safer than storing it in source code online but can still be compromised by someone who has access to your computer.

Start by right-clicking the project name inside the Solution Explorer window. The context menu will have an option to Manage User Secrets.

Project context menu shows option to manage secrets

Once you choose this option, Visual Studio will create an empty file named secrets.json inside your user folder. Now you can add as many secrets as you need inside this file. Each secret is stored as a key-value pair.

  "passwordForLifeAndEverything": "42"

All you need now is to read the value inside your code. This can be simply done by using the Configuration API.

string mySecret = Configuration["passwordForLifeAndEverything"];

When running the solution inside Visual Studio, it will automatically fetch the required data from your secrets file. Remember that this will only work for you on your own machine, and the other developers need to configure a similar setup on theirs. But the added advantage here is that everyone can have their own secrets, which is also better than sharing the same secrets with the whole team.

Secrets can also be grouped together by nesting JSON objects inside one another, such as the following secrets for a fictional mailing service.

    "MailService": {
        "Account": "dumdedum",
        "SecretKey": "SomethingLike8TGRKC4L71..."

Then you can refer to each key individually by using the colon character to symbolise the hierarchical relation between the keys, for example:

string mailAccount = Configuration["MailService:Account"];
string mailKey = Configuration["MailService:SecretKey"];

You can also read them together as a group, for example in the ConfigureServices method you can add


where MailSettings would be a class with the two mail service properties which will be automatically bound at runtime.

Secrets management is definitely an underestimated Visual Studio feature that tackle one of the most rampant credential dissemination issues inside the open-source world. This feature is also available as an extension in Visual Studio Code.

Azure DevOps provides a solution for visualising the test result history of your projects through the Dashboard. Let’s see how we can set up some charts.

Start by clicking the dashboard icon in Azure DevOps’ main menu.

Resource group access control

You can create a new dashboard if you wish, but in this case I will show you how to edit an existing one. So click on the Edit button.

Resource group access control

On the right-hand side of the screen, you should now see the Add Widget window. In the search box, type ‘test’ so that we can filter out the other widgets. Choose the Test Results Trend widget and click the Add button.

Resource group access control

This will add the widget, ready to be set up.

Resource group access control

Click on the cog icon to open the configuration window.

Resource group access control

Set it up as shown above. The important part is the selection of the build pipeline that is running the tests. Clicking the save button will produce the following chart.

Resource group access control

As you can see, it shows both the number of passed/failed tests as well as the test run duration. You can hover your mouse pointer over each column to get more information about the specific test run.

Resource group access control

Let’s add another widget. This time we’ll choose the ‘Test Results Trend (Advanced)’ widget instead. This widget allows us to view the trend over a particular period of time as opposed to the trend by build which we just saw with the other widget.

Again configure the widget as shown below.

Resource group access control

This produces the following chart, which is not particularly interesting when tests are running smoothly. Note that in some widgets, you can expand the view by clicking on the expand icon to get a larger view of the chart.

Resource group access control

This is an example from another project where there are occasional failures.

Resource group access control

That is how test charts can be added to your dashboard so that the whole team can keep an eye on the test results. Don’t forget to save the dashboard layout by clicking the ‘Done Editing’ button.

Resource group access control

Now it’s time to get serious. After adding an App Registration in step 1, giving it access to the Web App resource group in step 2, and preparing the Storage Account in step 3, we are now ready to finish the process by installing and configuring the Let’s Encrypt extension.

Let us head over to the web app where we will install the SSL certificate. Choose the relevant app from the App Service plan menu as shown here:

Finding the app settings

Inside the app itself, click on the Custom domains menu:

The Custom domains menu

And proceed by clicking the + Add custom domain button. Fill in your domain name.

Adding a custom domain

In order to add the domain name, you first have to prove that you actually own the name. I will not go into the details of buying the domain name and forwarding it to Azure in this post, but here I show one way to claim the domain ownership by using a CNAME record:

Domain verification page

The green checkmarks indicate that the domain was verified successfully. At this point, we should also be able to notice that the newly added domain name is not secure.

This domain is not secure

It is time to fix this and conclude the tutorial! While still inside the web app, scroll down until you find the extensions menu and click it:

The App extensions menu

Then click on the + Add button:

Adding a new extension

Search for the Azure Let’s Encrypt extension by SKJP from the marketplace.

Selecting the Let's Encrypt extension

And accept the legal terms:

Read and accept the legal terms

After accepting the legal terms, the OK button is enabled and you can click it to start the installation process.

Click ok to install extension

You will get a notification of the installation status:

Extension installation in progress

Once ready you will be presented with the Let’s Encrypt configuration wizard. Here you need to fill in the information of the resources that we created earlier.

The Let's Encrypt configuration wizard

The information that you need to enter is obtained as follows:

  • Tenant id was shown on the last screenshot of part 1.
  • Subscription id is shown on the last screenshot of part 2.
  • Client id in the same last screenshot of part 1.
  • Client secret on the screenshot before it.
  • Resource group access was described during part 2. This is the same resource group that we configured there.
  • WebAppName we also saw during this post.
  • For both of the connection strings, refer to the last screenshot of part 3.
  • Finally, tick the checkbox for updating the application settings.
Double-check everything once over, and click the Next button to go to the next step.

Let's Encrypt settings are being applied

After some time, an information screen about the existing and available custom domains shows up.

Existing domains

Click next to go to the final step. Here you need to pick up the domain name that the certificate will be bound to. Enter an email address where you will receive future expiry warnings and click on the Request and install certificate button.

The certificate request step

After this, the extension will generate and install the SSL certificate for you.

Certificate was installed successfully

We should now be able to see this new certificate in the Certificates page inside theTLS/SSL settings menu.

Certificate list

Back in the custom domains page, refresh to see that the domain name associated with the new certificate is now secure.

Site is now secure

To confirm that the website is really secure, just hit it through the browser and click on the padlock icon. I’m using Chrome here so other browsers will display a different icon and have a slightly different behaviour.

Browser also shows that site is secure

Great! We have an encouraging message that the connection is secure. Clicking on the Certificate icon shown above will open the detailed certificate information window. Just like what the rest of our website visitors will see, here we can also read that the certificate was issued by Let’s Encrypt.

Certificate details

While we are still on the Azure portal one final thing. Since our HTTPS connection is now secured, we can switch on the option to force the secure connection. This is done again from the TLS/SSL settings page as shown here:

Switch to toggle HTTPS only mode

Now if anyone tries to access the site through the unsecured HTTP connection, they will be redirected to the secure site. We can observe and confirm this behaviour in the network headers.

Site should now redirect to HTTPS

So that concludes our journey to install a free SSL certificate to an Azure website through the Let’s Encrypt extension. Many thanks to SJKP for sharing this useful extension with the rest of us. If you have any further questions feel free to comment below.

Buy me a coffee Buy me a coffee