Add a route to your VPN connection via PowerShell

I’m sure that you’re using some VPN somewhere, and you’re having “trouble” with split tunneling and routing, right?

Well, I had. 😀

As I’m “here and there” most of the time, I’ve setup an “anchor” location (no, it’s not in the cloud… yet) which is always available via VPN, and which has few machines that I’m, more or less, using regularly. When I’m not there, I connect there via my precious Windows 10/11 laptop and work as I’m there locally. I know – you know what VPNs are used for… bear with me a bit longer. 😀

So, all good – I have a VPN client (Windows built-in), a VPN server and Internet connection, and I can work.

One thing that I like to have is Internet access which is not routed via my “anchor” location, so that “the work stuff” goes through VPN and “the fun stuff” not.

It’s really easy to set this up – in properties of your VPN connection, just untick the “Use default gateway on remote network” checkbox:

But then you’ll have an issue with connecting to “the work stuff” – your current default gateway doesn’t know where “the work stuff” network is and how to get there.

It needs a route.

No problem, it’s easy to add a route in Windows (my “the work stuff” network is and my VPN gateway is, or publicly

And now you have access to “the work stuff” network again! And Internet access works as it should (not via the “anchor” location)!


But then you disconnect. And reconnect. And route you’ve added is gone. So, you repeat the procedure. Or script it. Or…

What if I tell you there is actually a better way?

I’m not really sure in which release this came out, but now you have an updated set of PowerShell cmdlets in (Windows 10/11) (which is cool!). For this story, the one we’re interested the most is Add-VpnConnectionRoute.

“So, doest that mean that, with it, I can configure my VPN connection to always have the route I need, whenever I connect to VPN? No more adding routes manually?!”


If I use the discussed Add-VpnConnectionRoute on my existing VPN connection, I can add the route I need and it will be written in the connection configuration and made active when the tunnel comes up, while still using the split tunneling.

Let’s see:

  • connected to “the work stuff” VPN and this is (part of) routing table prior the route configuration:

  • adding route configuration:

  • checking routes again:

As you can see, I’ve got new routes in my route table (it would be the same by using route add command above) and now I can access “the work stuff” without any issue:

And if I disconnect and connect again – it still works! 😊

Hope it helps someone!


Checking certificate expiration with PowerShell

Had an idea to write some (PowerShell) script which will check and maybe notify me of certificates that are nearing expiration for a bunch of (public) sites that… somewhat matter to me. 😊

As it turns out, someone already had this idea and wrote very nice PowerShell script that does just that, available here – thank you!

While testing it, there were sites on which the script worked just fine, and there were sites on which I got errors like this one (Error: “String was not recognized as a valid DateTime.”):

Seems to be connected to my regional settings (I know… who would ever use hr-HR instead of en-US, but… 😊) and date/time formatting:

I’ve tried to fix it in a couple of ways, but the one that finally did it (for me) was explained on Dan Sheehan’s blog (thanks!), implemented on lines 25-26 below.

So, my adapted script looks like this (and works with my hr-HR culture):

It provides the following output (which can be further customized per your needs, of course… and I know – need to insert some line breaks, convert output to HTML, send it via e-mail, … it’s a start! 😊):

Note that I’m returning expiration date “the Croatian way”, by using the following formatting:

Hope it helps someone (and #kudos to original authors)!


UniFi Network Application on Ubuntu Server on Raspberry Pi 3 (arm64)

Another catchy title, right? 😀

Decided that I want to move my UniFi network controller to Ubuntu based installation, which will be running on top of Raspberry Pi (3) device which was collecting dust.

This can be easily achieved by following these steps:

  • take a backup of your current configuration (my controller was offline, so I’ve just copied the last automatic backup from /var/lib/unifi/backup/autobackup)
  • prepare the SD card with OS installation (detailed info here) – I’ve selected Ubuntu Server 21.10:

  • with prepared SD card, boot Ubuntu Server on your Raspberry Pi device
  • first login is ubuntu/ubuntu, and you’ll need to change password immediately after
  • next, you’ll probably want to set your Raspberry Pi to use static IP configuration – I’m using netplan to set it up:
    • just in case, I removed all *.yaml files inside /etc/netplan/
    • create new netplan template (YAML file) called 00-eth0.yaml in /etc/netplan/ (watch those white spaces!)

    • apply the configuration (your IP address will be reconfigured, so you’ll also lose the current SSH connection, if connected remotely(!))

  • install the UniFi Network Application:
    • there is a nice official guide here, once you take care of prerequisites, but basically:

  • restore from backup and start using it:

Note: If you’ll check your unifi service status, you may see “WARN Unable to load properties from ‘/usr/lib/unifi/data/’“:

This can easily be resolved by enabling the built-in “uncomplicated firewall” (ufw) – don’t forget to open ports you’ll need when it’s active! (such as SSH (22/tcp), inform endpoint for your devices (8080/tcp), UniFi Network Application web (8443/tcp), etc.):

* can be done more restricted, if needed
** full list of ports is available at – add them if needed (for instance, 6789/tcp is used when testing upload/download with mobile app)
*** be careful – it’s a firewall!

Note: If your access point is shown offline/timeout, maybe you forgot to open up the “inform port” 8080/tcp in UFW (been there, of course 😀):

And that’s it – you now have the fully functioning UniFi controller/UniFi Network Application running on top of your Ubuntu Server powered Raspberry Pi device!


P.S. Enable the autobackup feature… it’s useful (sometimes)! 😀

Beware of the proxy!

Had a (somewhat) interesting case the other day – after (finally) upgrading my Windows Admin Center (WAC) gateway machine to the new Windows Server 2022, my WAC suddenly stopped working. I couldn’t connect to any of the servers from within the console, couldn’t add new ones, … nothing.

When tried adding new servers, nothing happened – wizard stays at “Searching for…“:

Even PowerShell couldn’t connect anymore (which is actually the root cause of the above).

So, what happened?

Everything worked before and I wasn’t aware of other changes… other than upgrading my OS (in-place upgrade, Windows Server 2019 to Windows Server 2022), that is.

Let’s try and make sense of all this.

Test-NetConnection says everything is fine, Test-WSMan from another machine works:

However, Test-WSMan from this (WAC) machine simply doesn’t work:

Tried checking the logs next – two errors inside Applications and Services Logs -> Microsoft -> Windows Remote Management -> Operational log caught my eye:

  • Error 138: The client got a timeout from the network layer (ERROR_WINHTTP_TIMEOUT)
  • Error 142: WSMan operation Identify failed, error code 2150859046

So, it’s something with the network after all – more specifically, seems like there is some issue on the HTTP/S part!

After some thinking, I remembered that we have a HTTP/S proxy in our network – maybe my PowerShell session actually tries to go through it?! 😀

Checking if proxy is set (with netsh winhttp show proxy) – it is! This could be the issue.

Now I’m resetting the proxy settings (with netsh winhttp reset proxy, of course):

And then trying Test-WSMan again:

It finally works! And WAC works as well! 😀

Hope this helps!


What about this Bicep?

You’ve probably heard about Azure Resource Manager (ARM) – the deployment and management service/layer of Azure, which enables you to manage (create, configure, delete) your Azure resources. Also, you are probably aware that ARM uses so called ARM templates – basically, JSON files that actually define the infrastructure and configuration you want to deploy to Azure (think Infrastructure as Code, IaC).

So, if you have dealt with ARM/JSON in the past, you may have been finding it difficult to start with, and somewhat complex.

Bicep is here to help.

Here is a short overview of Bicep – basically, it’s a language which enables you easier deployment of Azure resources, without messing around (too much) with JSON. To be frank, it somehow reminds of Terraform, but it’s also different. It has many cool features, immediately supports all new Azure features and APIs, can be built (converted) into .json and deployed as such or it can be deployed straight away as .bicep, doesn’t require state file, it’s open and free, has great support in Visual Studio Code and much more. And it’s still in active development!

If you’re dealing with IaC and Azure, try it.

To show you the power (and simplicity) of Bicep, here is a short example of deploying Linux virtual machine in Azure (together with a resource group, virtual network, virtual network subnet, virtual NIC and network security group), done “the old way” (in JSON, which was actually converted from Bicep… it’s easier than writing JSON from the scratch) and then done via Bicep (“the right way”? 😀).

Additionally, you’ll see that I’ve tried to break stuff into modules – with more or less sucess. 😀

The ARM/JSON way (could be done nicer/shorter, with parameters inside .parameters.json… if you know what you’re doing – this is converted from Bicep and serves just for illustrative purposes):


The Bicep way:

Bicep seems a bit easier to read and shorter, right (while still doing basically the same thing)? 😀

If we deploy the .bicep files above (note that I’m deploying the “raw” .bicep file directly – which is cool!):

We finally get our resources:

So, where should you start if you’re new to Bicep?

I would certainly recommend starting with free and official Deploy and manage resources in Azure by using Bicep learning path on Microsoft Learn.

After that, you can probably pick up Freek Berson’s book Getting started with Bicep: Infrastructure as Code on Azure (first and only book on Bicep that I know of – really liked it because of the simple (yet effective) examples with storage accounts, it connects everything and flows naturally – building up “brick by brick” and not “jumping around”, just to show off what Bicep can do).

Another great resource are also the Bicep examples – there’s plenty to learn from them too!

Of course, you’ll also need to practice – install the Azure CLI or Azure PowerShell module, add Bicep and use Visual Studio Code for your first steps with creating, deleting, configuring and breaking stuff… powered by Bicep! 😀


Capturing network trace in Windows

Do you need to capture some network traffic on a Windows box for further analysis, but don’t want to install additional software just… everywhere?

I usually do.

If you didn’t know, Windows has built-in tool with which you can do just that – (among other things) capture network trace to a file for further analysis. The tool is called netsh.

So, how do you capture traffic with netsh?

It’s fairly easy (for more options, filters and such, you can always check the accompanying help content – netsh trace start ?):

If you look at the location where you’ve saved your trace, you’ll see two files – of those two files, MyTrace.etl is the one you want:

OK, but what do you do with it?

If you try to open it with, for example, WireShark, you’ll see it doesn’t work:

So… we have a trace file with which we can’t really do anything?!?

Not exactly!

If you have Microsoft Network Monitor (now archived, but can be found… on the Internet) or Microsoft Message Analyzer (now retired), you can open up and analyze your trace as you normally would:

If you already have WireShark on, let’s say, your workstation, and want to continue using it for the analysis, this trace needs to be converted to a format which WireShark understands (hope that one day we’ll have WireShark which opens such .etl files natively).

You can convert it by using the free tool called etl2pcapng.

It doesn’t require installation, and if you want to use the pre-compiled binaries, they are available under etl2pcapng releases.

So, convert your (netsh) MyTrace.etl to (WireShark’s) MyTrace.pcapng with this command:

Once converted, you can open the new file (MyTrace.pcapng) in WireShark, and do what you would usually do to analyze it:

Hope this helps!


Mastering Docker – Fourth Edition

I’ve got yet another book from Packt Publishing (thanks, Sanjana!) – this one is called “Mastering Docker, Fourth Edition“, written by Russ McKendrick.

As this is already the fourth edition of a book, it can’t really be bad, right?!

Spoiler alert… it isn’t! 😊

I liked this book because it covers pretty much everything you’ll need to get started with containerization and Docker… and much more!

Really liked the format and the direction it takes – first part starts with introduction to Docker and why actually you may need it (and let’s face it – in today’s world, everything is about containers and you need Docker or something similar!), then goes through usual tasks of preparing the environment for Docker, building and distributing images, and finally – managing containers brought up from those images.

Next, it introduces Docker Compose for multi-container apps and Docker App for sharing Docker Compose bundles (something that’s been missing so far, and people at Docker recognized and built it recently). Following chapters cover bootstrapping of Docker hosts, which is also helpful and good to know.

Second part of the book covers mostly clustering (Docker Swarm), introduces Kubernetes and explores options for running containers in the public clouds, while the last part of this book covers best practices, security, monitoring and other important things you need to know when dealing with containers.

Overall – I really liked the practical examples and the whole “flow” of the book (from introduction to more complex parts of a subject). Also, I liked the broad picture presented in this book, because containers are really a broad subject nowadays… there is so much to learn and so many options.

For people that are dealing with containers for years now and have deep understanding topics surrounding them, this book will maybe be “too shallow”, because it’s written broadly and you can’t put everything there is about containers, Kubernetes, Docker, clouds, … in one book (I mean – you could, but who would then read it? 😃). So, it’s on you to decide where you stand and what do you need.

Lastly, I think author did a very well job covering everything you need to start with Docker and containers, content is up-to-date, and would recommend you to go through this book if you have a chance. It would also be a good start for those taking a Docker Certified Associate exam, I believe.

You can order this book here.

Also, feel free to leave a comment of your impressions below, if you’ve read it already.


Patch Tuesday with PowerShell

It’s actually Wednesday here, but yesterday was another “Patch Tuesday” and Microsoft released its update packages.

So – it’s time to patch! 😊

Not sure how you’re patching your machines, but just wanted to make you aware of the nice PowerShell module called PSWindowsUpdate.

You can read more about it on the official PowerShell Gallery page and also here.

In short – this module takes care of controlling Windows Update from within PowerShell on your local and also remote machines.

And… I know it’s not nice like the Windows Update screen, but it does its job! 😊

To make use of it, you’ll have to take care of some minor prerequisites and install it via PowerShell Gallery.

Once done, you can use it to control Windows Update:

To make it easier for you, here is the installation script which takes care of… everything:

And that’s it – we’re done!

Happy patching!


Bad Request for url (error 400) in AKS

I’ve decided to go through the **awesome** AKS Workshop on Microsoft Learn and had some issues (with my setup), which I wanted to share, in case someone else hits them.

It was all good until I got to the part of creating the AKS cluster with Azure CLI – I was using Windows Terminal with WSL (Ubuntu 20.04) instead of using Azure Cloud Shell as suggested. I’ve gone through the steps of preparing variables needed for creating the cluster as it says, and when I tried to finally create the cluster by using “az aks create” command, I’ve got an error:

Error states that something is wrong with our request and neither –verbose or –debug options were giving me any useful details (actually, it was in front of me all the time, but I didn’t see it 😊). I’ve rechecked/reset the variables, tried once more and once more… it was all the same. As Google was conveniently down at the time (who would say, right?!), I’ve had to try and figure it out by myself. So, I’ve looked at the error once again:

Operation failed with status: ‘Bad Request’. Details: 400 Client Error: Bad Request for url:<subscription_id>/resourceGroups/aks-workshop/providers/Microsoft.Network/virtualNetworks/aks-vnet/subnets/aks-subnet%0D/providers/Microsoft.Authorization/roleAssignments?$filter=atScope%28%29&api-version=2018-09-01-preview

… and then it struck me!

There’s some trash in the URL (more precisely – my AKS subnet ID was having “%0D” added to the end)!

And if we check what “%0D” exactly stands for, it says “carriage return” (which I’ve obviously didn’t want to be a part of my subnet ID) – so, even it all seemed fine when looking at the variable content, now I know it wasn’t.

Easy-peasy, we can fix the part where we’re extracting this subnet ID or we can just replace the variable’s value with the right one (without the %0D at its end, that is).

That got me going… towards the next error. This one was actually more descriptive (yes, and the first one is descriptive enough, if you read it carefully 😊) – it said that I’ve got additional content inside my Kubernetes version variable:

Operation failed with status: ‘Bad Request’. Details: Error to parse agent pool version “1.19.3\r”: Invalid character(s) found in patch number “3\r”

You can see the extra “\r“, which again, is here because of bad value assigned to the variable $VERSION.

Which can also be easily fixed.

One other funny thing I’ve observed was, when getting my Kubernetes cluster credentials, as you can see below, they were actually merged to C:\Users\tomica\.kube\config:

This was funny because I’m inside WSL… which doesn’t actually have C:\Users\tomica\.kube\config, right? (and no, credentials weren’t merged to /home/tomica/.kube/config, which kubectl there uses by default, so… they are actually at /mnt/c/Users/tomica/.kube/config – funny, will check with the MS folks) 😊

Fair enough – we can merge them manually or just select the right file and we’re good to go:

There you go – if you get stuck on similar things, maybe this can help you. 😊


Yet another “Kubernetes with Raspberry Pi” post

There’s a ton of the tutorials on how to get Kubernetes installed onto your Raspberry Pi, so… let’s write another one. 😊

As mentioned in my last post, I’ve found my forgotten Raspberry Pi, and played around with installing and configuring Raspbian Buster on it.

Today, I wanted to check if it will be possible to install Kubernetes onto such small machine – they are many articles on the “widest of the world’s webs” that say “Yes, it can be done!“, so I’ve decided to give it a try! And I chose to follow one of them (seemed like a nice reference).

As you remember, I’m starting with a cleanly installed (and just slightly customized) Raspbian Buster and building it from there.

And I’ll be using kubeadm for installing my cluster.

So, once I had at least two machines (my Raspberry Pi for the “control plane” and Ubuntu 20.04 LTS Hyper-V virtual machine as the “node” – you can read more about it here), I prepared them like this:

  • install Docker (in my case)
  • change the default cgroups driver for Docker to systemd
  • add cgroups limit support (for my Raspberry Pi 3)
  • configure iptables
  • disable swap (this one was a bit challenging)
  • prepare for Kubernetes installation (source, keys, kubeadm)
  • install Kubernetes “control plane”
  • add flannel
  • add a node to the cluster
  • test with some workload

One thing that bothered me (on Buster) was disabling swap in a way that it also stays disabled after a reboot (I know, it’s the details that eventually get you) – after a while, I’ve stumbled on this forum post and the solution provided by powerpetedid the trick! Thank you, @powerpete! 😊

And finally, details about the each step are here (outputs are commented and somewhat redacted/condensed):

Seems to be working (😊):


P.S. I’ve read about some having issues with flannel and using other network options (didn’t have this one). Also, if you’ll have issues with iptables (v1.8+), maybe you’ll need to switch to legacy version (didn’t have this one either).