Some time ago I had to validate who or what has access to the applications we created in our Azure environment.
There were hundreds of different applications with each their own specific Application Roles. Both users and service principals had roles assigned to the applications to perform the required operations.
It is possible to click through every application in Entra ID and validate the assigned roles. However, this takes quite a bit of time.
So I figured “Is it possible to iterate through all my applications in Entra ID and see who or what has an application role assigned?”
Needless to say, the answer is “Yes!”.
By using the Azure CLI and the Graph API I was able to accomplish what is required.
#########################################################
# Login if running local and you haven't done so already
#########################################################
Function Login {
# Subscription used for logging in. Necessary to get a context to work in.
$loggedInSubscription = "The subscription name"
$tenantId = "73fefcf4-c062-45ef-9607-e19aba33f82d"
az login --tenant $tenantId
# # Log in to a subscription which resides in the tentant we want to add & configure MG's to.
az account set --subscription $loggedInSubscription
}
Function Get-AppRoleAssignments {
[CmdletBinding()]Param()
Write-Verbose -Message "Starting to retrieve all Enterprise Applications matching the naming convention."
$roleAssignmentsForAllApplications = $null
$enterpriseAppRegistrationCollection =
az ad sp list --filter "startswith(displayname, 'jv-sample-app1') or startswith(displayname, 'jv-sample-app2')"
| ConvertFrom-Json
Write-Verbose -Message "Found $($enterpriseAppRegistrationCollection.Length) enterprise applications."
foreach ($enterpriseAppRegistration in $enterpriseAppRegistrationCollection) {
$servicePrincipalAppRoles = $null
$assignedAppRolesForServicePrincipal = $null
# The service principal from 'az ad sp list' already has all the data we need
$servicePrincipalAppRoles = $enterpriseAppRegistration.appRoles
Write-Verbose -Message "Found $($servicePrincipalAppRoles.Length) appRoles for $($enterpriseAppRegistration.id)."
$assignedAppRolesForServicePrincipal =
az rest `
--method get `
--uri https://graph.microsoft.com/v1.0/servicePrincipals/$($enterpriseAppRegistration.id)/appRoleAssignedTo
| ConvertFrom-Json
| Select-Object -expand value
Write-Verbose -Message "Found $($assignedAppRolesForServicePrincipal.Length) assigned identities for $($enterpriseAppRegistration.id)."
$roleAssignmentsForAllApplications += foreach($assignedAppRole in $assignedAppRolesForServicePrincipal) {
$role = $servicePrincipalAppRoles | Where-Object{ $_.id -eq $assignedAppRole.appRoleId}
Write-Verbose -Message "Adding $($role.displayName) to collection for $($assignedAppRole.principalDisplayName) on application $($enterpriseAppRegistration.displayName)."
New-Object PsObject -Property @{
AppRoleId = $assignedAppRole.appRoleId
AppRoleDisplayName = $role.displayName
PrincipalObjectId = $assignedAppRole.principalId
PrincipalDisplayName = $assignedAppRole.principalDisplayName
ApplicationDisplayName = $enterpriseAppRegistration.displayName
ApplicationApplicationId = $enterpriseAppRegistration.appId
ApplicationObjectId = $enterpriseAppRegistration.id
}
}
}
return $roleAssignmentsForAllApplications
}
#Login
Get-AppRoleAssignments -Verbose | Sort-Object -Property ApplicationDisplayName | Format-Table -Property ApplicationDisplayName, ApplicationApplicationId, ApplicationObjectId, PrincipalObjectId, PrincipalDisplayName, AppRoleId, AppRoleDisplayName | Out-File "AppRoleAssignments.md"
The output looks pretty much like this
Read more →The project I am working on requires me to deploy our compute solution, .NET and Python, to an Azure service and it should only expose specific endpoints via Azure API Management (APIM). To accomplish this, I have set up some networking services including NSG-rules. The goal is to set up the network boundaries as strict as possible.
One of the things I started with is set up APIM in a subnet, the Container Apps in another subnet and use an NSG to limit traffic to only use port 443. This is based on the knowledge of my containers and what is mentioned on the overview pages of the Container Apps networking page.
┌─────────────────┐
│ APIM │
│ (Subnet A) │
└─────────────────┘
│
│ HTTPS Traffic
│ Port 443
│
│ NSG: Allow
│ Port 443 Only
│
▼
┌─────────────────┐
│ Container Apps │
│ (Subnet B) │
└─────────────────┘
Long story short: This does not work!
When trying to invoke any endpoint on the Container App via the APIM test page I continuously received an error.
Error occured while calling backend service.", “connection timed out: 10.0.6.139:443
As the IP-address is resolved, I know the DNS resolution works. Just a timeout on port 443.
Obviously, I’ve exposed port 443 in my containers. That’s the first thing you should probably validate when running into this issue.
Read more →I had the opportunity to get a new development machine and it had to be a laptop. I’ve used solely Windows machines in the past and was always annoyed to see my peers, using a Mac, have awesome battery life and could have their laptop on their lap or table without melting the surface and never have fans turning on when doing simple stuff.
That’s a couple of reasons I chose to get a Macbook Pro myself this time. Of course, having the laptop integrate with all my other Apple hardware at home is a nice perk too.
What did I get
At this moment, a Macbook Pro comes with an M4 chip.
Based on what I’m reading on the internet, you can’t go wrong with any of these chips. Be it the M4, M4 Pro or M4 Max. I did read the fan turns on from time to time with the M4 Max, but that kind of makes sense. The M4 Pro machine was within the budget I was looking for, so chose that one.
There isn’t much to choose from when you know the size, 14", and CPU you want. I went with the 48GB memory and 1TB of storage option and that’s the machine I’m typing this post on right now.
Read more →There’s a new feature over here, my weekly links archive.
Every week a page will be added automatically based on content I have read and found interesting to share. I’m using a self-hosted Linkwarden instance to collect pages and links for a variety of topics. For the purpose of this weekly links archive I have created a new tag called Newsletter which I use to fetch the weekly links to share with all of you.
Of course, I don’t want to make my Linkwarden service open to the internet. To fetch the content from Linkwarden in my scheduled GitHub Action I use Tailscale as shared earlier on this blog.
Query Linkwarden API
Linkwarden provides a nice API that is documented (somewhat) on their site: https://docs.linkwarden.app/api/api-introduction
Not all features and possibilities are mentioned over here, but with some creativity you’ll be able to figure out a lot.
To use the API you first need an access token.
These tokens can be created on the Settings -> Access Tokens page.

Once you have this token, make sure to store it somewhere safe.
As you can see in the documentation there’s a search endpoint. This one is useful to validate your requests are working. The endpoint I’m most interested in is the links endpoint and using the tagId querystring parameter. To make good use of this, you do need to know which tag to query, in my case it’s 5.
Do note, this endpoint is being deprecated in favor of the search endpoint. At this point, I have not been able to figure out how to use the search-endpoint to retrieve all posts for a specific tag, but this will probably be necessary at some time in the future.
Read more →I have been a long time user of Tailscale, a very easy to use VPN mesh system, to connect to my home network when I’m not at home. There are other solutions, like NordVPN Meshnet, but I learned about Tailscale first. It’s especially useful when using your local DNS (PiHole) and taking advantage of the sites blocked by it.
Currently, I’m in the process of self-hosting services a bit more. Relying in (free) cloud services that might be turned on/off at a moment notice or removing features I use is something I’ve seen happen a bit too much lately.
Because of this, I’m running a container of Linkwarden to act as a ‘Read it later’ or bookmarking service. I was using Omnivore for this, but that service got shut down at some point.
Some of the links I store in Linkwarden might also be interesting for others who might have missed them. I wanted to get a list of these links and share them on my site, which is why there’s now a Weekly Links Archive. These pages contain all links I find interesting enough to share over here.
Because I’m a firm believer in automation, these pages are created via a GitHub Action workflow.
Read more →In an earlier post I wrote about using Semantic Kernel to create an Agentic AI solution, all using C#. Of course, similar flows can be created with Python. To try this, I’ve created a sample solution to update a resume so it’s more likely to pass the ATS requirements used by various companies nowadays.
My sample is heavilly inspired by Gian Paolo Santopaolo his CV-Pilot repository, which I was not able to use due to the CrewAI tooling phoning home and my DNS (PiHole) blocking those requests. Even after disabling the tracking features, the library/ies still tracked ‘something’, which caused the logic to break so I decided to create something myself using Semantic Kernel.
What did I create?
A tool/flow to update a resume so it can pass the ATS (Applicant Tracking System), based on the job description. These systems ofen check for specific wording and skills. For a human, it takes quite a bit of time to (re)write a resume to pass the ATS requirements. It’s the perfect job for a LLM to perform, as it’s built to create text based on other text input.
You can try doing this in a single prompt, but there’s a high probability this won’t perform the way you like. In my sample, I’ve created a ‘Project Manager’-, ‘Job Market Analyst’- and ‘Strategist’-agent. Each with their own specific job and goals.
Read more →We are finally at a state in the GenAI-space where we can create agentic AI solutions with ease.
I’m most familiar with Semantic Kernel, when working with LLMs, and this library works great for creating these solutions.
In a nutshell, what you need to do is create a group chat, add your agents to it, and then let them work together to solve a problem.
Do keep in mind, at the time of this writing, version 1.58.0 of the Semantic Kernel library is used. Development is going fast in this space, so behavior might change in future versions.
For my proof of concept, I’ve created a simple solution capable of creating a Fibonacci sequence, validate if a number is part of that sequence or answer random questions you would also ask to a regular LLM-powered chatbot. If you’re interested, the full sourcecode can be found on GitHub in my console-agent repository.
Create the agents
Agents are created with the ChatCompletionAgent class. You should provide the instructions & the kernel to use.
The instructions is just a regular prompt we all know and love when working with an LLM and specifies what the agent should do. I won’t repeat it over here as it’s not relevant for this post.
Read more →I’m very happy GitHub Copilot exists and lately with the Agent-mode it’s even better. It’s making sure I can focus on the relevant pieces of my solutions and not have to worry too much about the plumbing part.
The models it’s using are quite powerful and contains a lot of (old) data. When using new libraries or versions of already existing libraries, the LLMs used under the hood often don’t provide useful suggestions or edits. Currently, telling GitHub Copilot to check the repository of the library to as reference material doesn’t work the way I’d like it to.
I’ve tried this, but it only fetched the contents of the readme file of the repository and didn’t traverse all files and searching for relevant bits.
What I’d like is for GitHub Copilot to index the entire repository/repositories of my libraries of choice and provide me suggestions or edits based on the codebase or documentation.
I figured this can be achieved by adding this capability as an MCP server. Turns out, I’m not the only one who wants this. Leonid Bugaev has created an MCP server solution, docs-mcp, that can be used to search through repositories. It’s powered by Probe, also created by him.
Create an MCP server to search in repositories
The readme of the docs-mcp repository contains all the information to set up your MCP server.
Read more →When creating solutions for a company, you often use an internal package feed.
There might come a time when you need to provide the source code to an external party or you want to make the solution open source.
If this has ever happened to you, you know one of the first things to validate if all dependencies (NuGet packages) are available to the public. Especially in large corporations it’s easy to use some platform packages used throughout the company but should not be shared with the public.
I recently had to so one of those exercises. And instead of manually searching for all packages on NuGet.org, I created a small PowerShell script to search for the packages and version in the feed.
# Path to the Directory.Packages.props file
$filePath = "C:\Projects\Internal\MySolution\Directory.Packages.props"
# Load the XML file
[xml]$xml = Get-Content $filePath
# Iterate through each PackageVersion element
foreach ($package in $xml.Project.ItemGroup.PackageVersion) {
$packageName = $package.Include
$packageVersion = $package.Version
# Check if the package exists in NuGet
$result = nuget list $packageName -Source https://api.nuget.org/v3/index.json
if ($result -match $packageName) {
Write-Host "Package '$packageName' (Version: $packageVersion) is available."
} else {
Write-Host "Package '$packageName' (Version: $packageVersion) is NOT available."
}
}
By running this, you can see in the output if a package is or is not available for your external consumers. It’s not rocket science but has sure saved me hours of searching for this information manually.
Read more →When working on a project that’s being distributed you are often required to create a NOTICE file giving the necessary attribution to work you rely on. As written on the Apache site on this topic:
If the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
It’s not hard or complex to create these files, but it’s a LOT of work when starting.
Read more →