Create a self-signed certificate for your web server with PowerShell

Sometimes you may need SSL certificate just for testing your (local) web application. Of course, for public and trusted purposes, you’ll probably use free Let’s Encrypt certificate or something similar (or, of course, any of the paid options).

And this is OK as long as you have publicly resolvable domain name.

But what if you need certificate for, let’s say, “localhost” or “webserver.local”?

Then you’ll probably use your internal PKI infrastructure or a simple self-signed certificate.

Second one can be easily achieved with PowerShell, by using the New-SelfSignedCertificate cmdlet (or with OpenSSL, yes ?).

So, let me show you how.

We have a simple IIS setup hosting a single (default) website, responding to http://localhost/:

We’ll issue a new self-signed certificate, make it trusted (important!) and then attach it to our test website, with following:

If everything goes well, we will see another binding created in our IIS console:

And if we open https://localhost/, all should be good as well:

Cheers!

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 192.168.13.0/24 and my VPN gateway is 192.168.14.1, or publicly 141.138.55.154):

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

Great.

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?!”

Exactly.

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!

Cheers!

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)!

Cheers!

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!

Cheers!

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!

Cheers!

Utilman.exe to cmd.exe and back

Let’s say you have a Windows (virtual) machine, for which you’ve forgotten your login info, but you want to enter it anyway, because of… reasons. ?

How can you do it?

Note – if the disk/VM is encrypted, you’ll need the decryption key, of course (if you don’t have it, well… I’m sorry, the following won’t really help you).

Ok, if it’s a virtual machine and you only need to grab some data from it, it’s relatively easy – you’ll just mount the virtual disk, extract the data needed and done.

If you need access to the OS instead, you can maybe use the old trick with replacing the Utilman.exe with cmd.exe, which essentially gives you command prompt with local system permissions, which then gives you… well, everything you need.

One minor obstacle with doing this “hack” would be the fact that the owner of Utilman.exe is actually the TrustedInstaller, so your workflow would be like this:

  • (e.g. turn off the VM, mount the disk, …)
  • replace the owner of Utilman.exe
  • add yourself the needed permissions
  • replace the Utilman.exe with cmd.exe
  • do what you need (e.g. change the local Administrator’s password, set this account as active, …)
  • cleanup (replace the replaced Utilman.exe with the original one)

And we can do this with PowerShell:

And now you can login as local Administrator again and do the work you wanted to do in the first place. ?

To leave things in (somewhat) the way we found them, we can use the following PowerShell:

Cheers!

Network share feeds in WAC

You know about (and actively using) the Windows Admin Center (WAC), right?! ?

While it’s great for managing your Microsoft infrastructure, it can also be extended with different extensions. You can even write and use your internal, custom extensions, which do… well, whatever you make them do. And you can read all about that here.

But let’s go back to the subject of today’s post – extensions can be installed via different feeds, either official or unofficial, provided by Microsoft or 3rd-party. You can easily add new feeds or remove existent by providing the feed location, which can be either a NuGet feed URL or a file share location, as stated in the official docs.

Using a file share location is easy:

  • you choose/create a folder:

  • share it (\\<my_server_name>\WACExtensions in my case):

  • and add it to your feeds – I’ll use the “PowerShell way”:

But no!

My feed seems to be added successfully, but it’s not showing in the list!

You can try the same through the web interface – it’s almost the same (OK, you’ll get the errors):

And permissions are fine, don’t worry. ?

Why’s that?!

The catch here is that we added an empty folder/share – when adding this share, WAC intelligently looked into the folder, found nothing and (successfully) didn’t add our share to the feed list, as it’s empty. And yes, it also forgot to mention it when using PowerShell.

So, what can be done?

The workaround/solution is rather simple – just make sure you don’t add an empty feed/folder.

Just for fun – I’ve downloaded the HPE Extension for WAC, moved it into the WACExtensions shared folder and tried to add the feed again:

And – it worked! ?

Cheers!

Having fun with Helm and file encoding

Had some spare time, so I’ve tried to learn a bit more about Helm, the package manager for Kubernetes.

I’ve decided to follow the relatively new Pluralsight course called – Kubernetes Package Administration with Helm, done by my MVP colleague Andrew Pruski. And it was great – not too long, clear and easy to follow, with only a handful of prerequisites if you want to follow along! Great job!

Of course, there is also the nice, official documentation.

But why am I writing this post?

I was normally following this course on my Windows 10 laptop, using Visual Studio Code, as suggested, and also using PowerShell terminal, with Helm v3.3.1.

It all went well until the part when we are creating our Helm Chart, more specifically – when we’re filling up our deployment.yaml and service.yaml files. Suggested (and simplest) method is to use the simple output redirection (with “>“), like this:

But, this gave me the following error when trying to deploy the chart:

It’s quite obvious – Helm works with UTF-8, and my .yaml files seem to be encoded differently. Quick look at the bottom of my VSCode confirms it:

How can I fix it?

As I’m using PowerShell, it’s pretty easy – instead of doing the simple output redirection (“>“), I pipe output to Out-File cmdlet with -Encoding UTF8 option, in all cases, which takes care of the encoding (and sets it to UTF-8 with BOM, which is just fine for Helm):

So, long story short – if you run into the error above (Error: unable to build kubernetes objects from release manifest: error parsing : error converting YAML to JSON: yaml: invalid leading UTF-8 octet”), remember to check your file’s encoding (and change it to UTF-8, if needed)! 🙂

Cheers!

P.S. Thanks to good people at Pluralsight for providing me a complimentary subscription!

Fixing Hyper-V virtual machine import with Compare-VM

Well, I was rearranging some stuff the other day, and come to an interesting “lesson learned”, which I’ll share. 🙂

In my lab, I’ve had a Hyper-V server running Windows 2012 R2, which I finally wanted to upgrade to something newer. I’ve decided to go with the latest Windows Server Insider Preview (SA 20180), just for fun.

When trying to do an in-place upgrade, I was presented with the message “it can’t be done“, which is fine – my existing installation is with GUI, the new one will be Core.

So, evacuate everything and reinstall.

In the process, I’ve also reorganized some stuff (machines were moved to another disk, not all files were on the same place, etc.).

Installed Windows, installed Hyper-V, created VM switches, but when I tried to import it all back (from PowerShell… because I had no GUI anymore), I was presented with an error.

Error during virtual machine import was (I know – could’ve used more specific Import-VM command, which will select all the right folders and required options, but… learned something new by doing it this way!):

So, the error says it all – “Please use Compare-VM to repair the virtual machine.” 🙂

But how?! 🙂

If you go to the docs page of Compare-VM, you can see how it’s used.

And, in my case, the whole process of repairing this virtual machine looks like this:

Hope this helps you as well!

Cheers!

Figuring out your public IP address with PowerShell

Sometimes, you need to know your public IP address because of… reasons. My particular reason was creating firewall rule to limit SSH only from my current public IP address, to a machine on the Internet. And how to do it?

You can always use free services like What Is My IP?, which shows you your public IP address in a nice form:

But there are also other ways – if you’re running Linux (or WSL) and do a Google search for the command that can help you, you’ll probably get this (https://askubuntu.com/questions/95910/command-for-determining-my-public-ip?noredirect=1&lq=1):

And if you’re using Windows, PowerShell is here to help you! I like “oneliners”, even if they are not always easy to read:

I’m sure that my friend Aleksandar (PowerShell guru & Microsoft MVP) has a better way, but for me, this works just fine. 🙂

Hope it helps!

Cheers!