Tuesday, April 15, 2025

Azure Bastion Developer - shared Bastion pool

 The Azure Bastion is useful for connecting securely to virtual machine without exposing the VMs to the internet using public IP's.

This however, requires a dedicated Bastion instance to be deployed and an AzureBastionSubnet.

With Bastion Developer, test/dev users can connect to the local IP of the VM via Bastion using a shared pool, so no dedicated setup required. The features are limited though. See here for more info.

To use, in the portal simply go to the VM -> Connect -> Bastion.

Add credentials and click Connect. This will open a remote session in the browser, see below:








Azure: Troubleshoot connectivity to a key vault with a private endpoint

 If you have a key vault that you can't reach there can be multiple reasons for this. Two of the main ones are DNS issues and firewall blocks.

This post will go over those two issues and show a couple of ways to test for connectivity.

When working in hybrid setups with an on-prem location connected to Azure either via VPN or ExpressRoute then it happens that you can create and see the key vault (this also goes for e.g. storage accounts and other PaaS services) but you get an error when trying to add a secret or other content to it. The error can mention e.g. "the connection to the data plane failed".

To troubleshoot, first we ensure that the local IP of the private endpoint can be resolved.

nslookup myown-keyvault.vault.azure.net

On Linux you can use the command dig to get slightly better lookup details than with nslookup:

dig myown-keyvault.vault.azure.net

This should resolve to a local IP. If it doesn't resolve at all or if it returns a public IP, there is something wrong with the DNS setup.

More info on troubleshooting DNS can be found here.

If that works, you can check for connectivity from your source. This can be done in a couple of ways and either of them is fine.

From Windows run:

tnc myown-keyvault.vault.azure.net -port 443

From Linux run:

nc -zv myown-keyvault.vault.azure.net 443

All data to a key vault goes over port 443, so if you have connectivity on that port and it can resolve the IP, then you should be good.

Alternatively try:

$(Invoke-WebRequest -UseBasicParsing -Uri https://myown-keyvault.vault.azure.net/healthstatus).Headers

Or from Linux:

curl -i https://myown-keyvault.vault.azure.net/healthstatus

There is more info on troubleshooting behind a firewall here.

Thursday, April 10, 2025

Azure: Adding KQL queries as Resource Graph queries

 Using the Resource Graph Explorer can be helpful to retrieve information about your Azure environment using KQL. If you have certain queries that you run on a frequent basis and want to save, this can be done under Resource Graph queries.

If they are saved as 'shared' queries they can be viewed and run by others. Private queries can only be seen by you.

Resource Graph queries can be added via code and it is also supported by Azure Verified Modules.

The below screenshot shows what it looks like, when a few queries have been added:


If you click one of the queries, it will show the query itself in a new window:


And on the Results tab, you can run the query directly:


The Bicep AVM files are available on Github, see link.

The queries can be placed in any resource group.

Here are some examples of more queries from MS.




Tuesday, April 8, 2025

Retrieve custom DNS server settings on VNets using Kusto (KQL)

 You can use Kusto Query Language (KQL) to retrieve information about your Azure environment across subscriptions.

It's a fast tool and very useful in larger environments with many subscriptions and resources.

The following example shows how to list all VNets in the environment including their custom DNS server settings.

Go to Azure Portal -> Azure Resource Graph Explorer

In the query window, add the following KQL:

Resources
where type == "microsoft.network/virtualnetworks"
extend dnsServers = properties.dhcpOptions.dnsServers
project id, name, dnsServers

Click: Run query

Result will look like below (custom DNS only set on one VNet):



You can download the content as a csv and import into xls for sorting and filtering.



Wednesday, November 20, 2024

Azure: Creating a route table in Bicep and addressing linter rule warning

 I was creating a route table in Bicep and using an example from MSFT, see example here.

The example uses the 'contains' function which throws a warning when deploying with a suggestion to simplify the syntax. Below you can see the warning:


The warning links to the Linter rule - use safe access page.

The suggested change can be seen below. It still performs the same function - it checks if the value exists, if it does, it uses it, if not it sets the value to null.


The updated example can be found on Github here (it uses routes for Databricks as an example, but can be replaced with your own routes).

Tuesday, November 5, 2024

Azure: Creating a naming convention for cloud resources

 Creating a maintaining a well functioning naming convention can be surprisingly difficult. And it can lead to many a heated discussion. One of the reasons for this is that there isn't necessarily one right answer and in the end it's about convention and just making a decision and sticking to it.

To help out with the standardization of naming resources in Azure, MSFT has a good article, under the Cloud Adoption Framework, which has suggestions for most of the Azure resources. It's a good starting point:

https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming

Friday, October 4, 2024

Azure: Create a Workbook for centralized NSG log search - using VNet flow logs

 NSG flow logs will be retired in 2025, see more details here, and replaced by VNet flow logs.

This article will describe how to install a new Azure Workbook that enables centralized NSG log search using VNet flow log data. Note that to be able to retrieve and view logs with this workbook, VNet flow logs must deployed and configured in advance (you can deploy the workbook up front, but it will return empty queries).

The workbook is based on an existing workbook for NSG log search that uses NSG flow log data (see NSG workbook here). The logic is the same in the new workbook, it just retrieves data from another source (or another table in the Log analytics workspace, to be precise).

First, let's view the final result. Below you can see a screenshot of the workbook:


To install the workbook, do the following:

Download the workbook file from Github.

Go to Azure Portal -> Monitor -> Workbooks -> Click '+New'

Click on the Advanced editor, see below:


Choose Gallery template, then paste in the workbook code and click apply, see below:


That's it, this will create the workbook.

To ensure that you can find the workbook via Portal -> Monitor -> Workbooks, you have have to go into settings and choose Azure Monitor as a resource, see below (this might be set by default):



If you link it only to the log analytics workspace, then you have to navigate to the workspace first and then choose the workbook menu to see it.

If done right, you can navigate to Portal -> Monitor -> Workbooks -> Workbooks tab -> Click workbook to view, see below:


Below you can see the main Kusto query that the workbook uses (this is also included in the workbook file itself):


Note that if you run this as a query it won't return any results.

If you want to run a Kusto query directly (that returns a similar result), do the following:

Go to Portal -> Monitor -> Logs - Choose Kusto mode (instead of simple mode) -> paste in the below query and click run:

NTANetAnalytics
| where SubType == "FlowLog"
| extend Protocol = case(L4Protocol == 'T', "TCP", L4Protocol == 'U', "UDP", L4Protocol)
| extend TimeGenCET = datetime_utc_to_local(TimeGenerated, 'Europe/Stockholm')
| extend TimeGeneratedCET = format_datetime(TimeGenCET,'dd/MM/yyyy [HH:mm:ss]')
| project-rename NSGFL_Version = FaSchemaVersion
| project-rename AllowedOrDenied = FlowStatus
| project TimeGeneratedCET, VNet = split(TargetResourceId,"/")[2], NSG = split(AclGroup,"/")[8], SrcPublicIps, SrcIp, DestIp, DestPort, Protocol, L7Protocol, FlowDirection, AllowedOrDenied, AclRule, NSGFL_Version, FlowEncryption, Source_subnet = split(SrcSubnet,"/")[2], Destination_subnet = split(DestSubnet,"/")[2]  

You can see an example below: