Split strings

  • Context

I worked recently on some Active Directory properties and needed to split a path.

$h = (
Get-ADUser UserName -Properties HomeDirectory
$h = '\\servername\homepath.username$'

The HomeDirectory is a System.String and my variable $h contains something like \\servername\homepath.username$ (there’s a dollar at the end to hide the share).

  • Problem

I’d like to split on the \ character and to do so, you need to escape it with another backslash character because it’s a special character for regular expressions.

The official documentation About Split says that the delimiter is evaluated as a regular expression if I use the highlighted 3rd syntax:
The Options parameter is optional in the syntax and defaults to the value RegexMatch
The doc says:

RegexMatch: Use regular expression matching to evaluate the delimiter.

If I use the split operator like this:

$h -split '\\'

$h -split '\\' | measure

[string]::Empty -eq ($h -split '\\')[0]
[string]::Empty -eq ($h -split '\\')[1]
[string]::Empty -eq ($h -split '\\')[2]
[string]::Empty -eq ($h -split '\\')[3]

I get 4 results. The first 2 results of the split operation are an empty system.string.
This is expected.

I want to capture the server name and the share name, not the backslashes and not the empty strings.

  • Solution(s)

My first solution followed the idea “just ignore and discard empty strings”:

# capture
$null,$null,$server,$share=$h -split '\\'
# display result

My second solution is based on the idea that “there’s probably a better way to skip these empty strings”:

# display result

If you’ve another great idea and want to share another (better?) way to skin the cat, please post it in the comments 🙂 .


[Quick post] Exchange (Online) team’s changes to the default policy about blocked file types

The Exchange (Online) team is about to change the default policy about blocked file types, meaning that OWA (a.k.a Outlook on the web) users will be impacted by this change.

Their message is quite clear and was published on this page https://techcommunity.microsoft.com/t5/Exchange-Team-Blog/Changes-to-File-Types-Blocked-in-Outlook-on-the-web/ba-p/874451

Almost all the most common extensions related to PowerShell are concerned: .ps1, .ps1xml, .ps2, .ps2xml, .psc1, .psc2, .psd1, .psdm1, .cdxml, .pssc. That said, neither .ps2, ps2xml nor .psc2 are used and recognized by PowerShell or pwsh. We just wonder where they got this list of extensions? 🙄 (from telemetry?)

If you’ve the habit to send these files by email, your recipient may not be able to download and open the sent files if he uses Exchange Online (EXO) (i.e. OWA / Outlook on the web).

If you are an EXO admin, you can override the default policy and set your own policy using the guidance in the above article mentioned

As a user, you can zip the files before sending them. Or, if you’ve a OneDrive (or a similar online storage) you can store these blocked files there.

The kind of change doesn’t add anything to the security of your endpoints. On any Windows, these file types have always been associated by default with notepad and not PowerShell .

If any user gets a PowerShell script (a .ps1) by email, when he downloads and opens it, the .ps1 script doesn’t get executed but it’s instead opened by notepad.exe and its content displayed in notepad.exe.

The attack surface of notepad is extremely tiny. As far as I can remember, there’s only 1 known vulnerability that was discovered by a Google Project Zero security researcher over the last few years. It isn’t notepad.exe that was actually vulnerable but a more subtle sub-component CTF (a.k.a. Windows Text Services Framework) used by notepad.

Get full control of Windows Update

  • Context

I’ve been invited to a strange meeting where 2 companies were talking about Windows Updates. The software manufacturer wanted to patch the computers once a year while the maintenance provider wanted to be never ever disturbed by an update or its reboot during a full week of production. I told the software company that this bad practice is called “patch and pray”. I told the maintenance company that’s hard to achieve nowadays due to “Windows Update as a Service” and that I’ll have a look.

While I understand the business needs of these companies, it seems that the behavior described by Brian Krebs in his recent blog post has already negatively impacted their subconscious mind.
Please allow me to quote his post:

Most Microsoft Windows (ab)users probably welcome the monthly ritual of applying security updates about as much as they look forward to going to the dentist: It always seems like you were there just yesterday, and you never quite know how it’s all going to turn out.


Nevertheless, it is frustrating when being diligent about applying patches introduces so many unfixable problems that you’re forced to completely reinstall the OS and all of the programs that ride on top of it.


So, three words of advice. First off, don’t let Microsoft decide when to apply patches and reboot your computer.


Secondly, it doesn’t hurt to wait a few days to apply updates.


Finally, please have some kind of system for backing up your files before applying any updates.

I usually recommend the same as Brian Krebs to any user who’s not involved in patch management.

  • Solution

To regain control on Windows Update (a.k.a. WU), the following solution allows you to turn WU on and off on demand.

All the screenshots below have been done on a Windows 10 Enterprise 1903 provisioned in Azure:

  • How to install

Right-click the Start menu, select “Windows PowerShell (Admin)”,
Copy/paste in your browser the link to the above gist or click on it. Once you’re on the gist, you should use the ‘Raw’ button and then copy the content of the page and paste it in a PowerShell script file. I used a.ps1 as a name below:

Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
notepad a.ps1

  • Demo

Let’s see what it looks like once it’s deployed.
You get 2 scripts on the desktop and the following settings in the system settings tiles:
The active hours are set to 6:00 AM to 11:00 PM.

It also defines a period of 185 days to defer upgrades and 25 days to defer updates. If that’s not what you want, you’ll need to modify the content of the script but that’s beyond the scope of this blog post.

If you use the Off.cmd script on the desktop, checking for updates fails (that’s expected)

If you use the On.cmd script on the desktop and hit “Retry”, checking for updates succeeds:

  • Known limits

The above solution doesn’t work in a corporate environment where the above WU settings are controlled by an administrator.
The above solution works only for an Ethernet connection because it’s set as a metered connection. That will also stop Office 365 updates (it honors the metered flag). Unfortunately the above solution isn’t working for a wireless connection.

How to delete a single Applocker rule

  • Context:

A colleague of mine was working on Applocker rules and the installer he was working on was so badly designed that we thought it would easier to restore the worst and most dangerous default rule named “*” for the built-in administrators.

If you restore this evil rule, you cannot and should not leave it there. You also have to delete it after it has been used to install successfully that ugly software.

  • Problem:

I started looking around using my google fu but only found the following web page from Microsoft named delete-an-applocker-rule that tells you actually how to clear *all* the rules. It’s a dead end and really not what we want 😦

  • Solution:

Instead I wrote the following function. Before looking at its code, let me tell you the following:

If you specify the first parameter to indicate what type of rules you want to delete, a Exe, Script, Msi, Appx, Dll, it will be used in the dynamic parameters block to enumerate all rules names found for this type.

Let’s see this in action:

You may also notice in the body of the function that the XML representation of Applocker rules never touches the disk. The rule is removed from the XML and its result is directly merged/reimported.

The function specifies a ‘high’ ConfirmImpact and SupportsShouldProcess because it’s destructive. There’s no backup of the rules or the rule being removed.

Last but not least, the rule is deleted but the group policies (GPO) are not refreshed. You’ll see its impact once the GPO have been reapplied.

Here’s the function:

Quick tip: uninstall a driver

  • Context

Before upgrading to the next Windows 10 branch, we thought it would be handy to have a script to update the drivers set installed on the computer if required.

  • Issue

We packaged many drivers set but we encountered an issue while upgrading the Intel Chipset drivers.
The newest drivers wouldn’t install on top the previous version. I think it’s due the the fact that the date in the inf file is in 1968.

  • Solution

To fix it, we first installed the new Intel chipset drivers. They have been made available into the local drivers store.

To force Windows to use them, I had to uninstall these drivers like this. Yes, there’s no cmdlet to remove a driver from an online image.

Get-WindowsDriver -Online | 
Where {$_.Version -eq ''} | Foreach-Object {
 pnputil.exe /delete-driver $_.Driver  /force 

Disclaimer: Be careful, you cannot always do that. Some drivers are more (boot) critical to Windows than others.
If you force the uninstall of a driver that was critical, Windows may not boot anymore.

About the Exchange Online (EXO) module and multi-factor authentication (MFA)

It’s officially documented on this page (you should start there)

The only thing missing on this page is what happens if you’re behind a proxy.

After the first use, you have an .appref-ms file sitting on your desktop that points to the URL below.

To get it right the first time, immediately, here are the basic steps

# Launch a new shell
saps https://cmdletpswmodule.blob.core.windows.net/exopsmodule/Microsoft.Online.CSE.PSModule.Client.application

# type the following in this new shell
$o = New-PSSessionOption -ProxyAccessType IEConfig
Connect-EXOPSSession -UserPrincipalName `
"my.account@tenantname.onmicrosoft.com" -PSSessionOption $o

The first visit to the URL above will download Microsoft.Online.CSE.PSModule.Client.exe somewhere under ~/AppData\Local\Apps\2.0\
If you don’t have an application whitelisting solution blocking it, you’ll see

Once executed, it will download whatever dependencies are required (System.Management.Automation.dll, Microsoft.IdentityModel.Clients.ActiveDirectory.dll, Microsoft.Exchange.Management.ExoPowershellModule.dll, Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll,…) and run the downloaded CreateExoPSSession.ps1 script that will display this new shell

# it runs this actually to start a new shell
C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NoExit -ExecutionPolicy RemoteSigned -File ~\AppData\Local\Apps\2.0\YO2PKAE1.DKY\GAOJE34E.RXK\micr..tion_1975b8453054a2b5_0010.0000_22e56f9efbc200c6\CreateExoPSSession.ps1

Then you can capture the proxy settings defined in your profile and use them when you need to connect to the remote endpoint that allows you to manage the remote Exchange online infrastructure.

The Connect-EXOPSSession function should be used with a UserPrincipalName parameter and not credentials. The UserPrincipalName allows you to get the MFA form while the credentials assume that you don’t have MFA enabled. If you use credentials instead of the UserPrincipalName, you get an error saying that you’ve MFA enabled and you didn’t go through the MFA validation process:
New-ExoPSSession : AADSTS50076: Due to a configuration change made by your administrator, or because you moved to a new location, you must use multi-factor authentication to access ‘00000002-0000-0ff1-ce00-000000000000’.

Enjoy EXO with MFA 😎

What kind of server channel are your running on: SAC or LTS?

  • Context:

I installed a SAC (Semi Annual Channel) based server and wanted to know what’s the difference with the LTS (Long Term Support) branch?

Source: https://docs.microsoft.com/en-us/windows-server/get-started-19/servicing-channels-19
There’s only 18 months support for the SAC channel.
There’s no upgrade path between branches, so you need to install from scratch when you want to move to a new branch.
That’s a big the deal.
I also wanted to have some code that would help me differentiate these 2 channels?

  • Solution:

I started to dig in google and the registry. I found this relevant info:

Source: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/hh846315(v=vs.85)

The WMI repository can help and contains the following:

Get-CimInstance -Property Caption -ClassName Win32_OperatingSystem |
Select -expand Caption

NB: Notice the year in the caption name before the edition (ex: Standard or Datacenter)
When the server is on a LTS channel, the year is specified whereas when on a SAC channel, it’s absent.

While there is a Get-ComputerInfo cmdlet, it doesn’t meet my needs. I wrote another function

The isExpired property is only valid for the SAC channel. I should modify the code accordingly in a further release.
If you run on the LTS channel, I would recommend that you use the official info on this site: https://support.microsoft.com/en-us/lifecycle/search

Let’s see the above function in action:

On a Windows server 2016

On a Windows server 2019

On a Windows Server 1903

On a Windows Server 1709

Notice that this SAC channel is expired

On a Windows 2012 R2 Server

Again, the LTS channel have an expiry date specified on this site: https://support.microsoft.com/en-us/lifecycle/search