Contact

Subscribe via Email

Subscribe via RSS/JSON

Categories

Recent Posts

Creative Commons Attribution 4.0 International License
© Rakhesh Sasidharan

Elsewhere

Managing Outlook rules using PowerShell

To view the rules associated with a mailbox use the Get-InboxRule cmdlet. Very useful when you are troubleshooting a remote user who is not getting emails and you suspect the rules could have a hand in it.

The cmdlet requires the initials of the user whose mailbox you want to check via the -Mailbox switch:

To view a particular rule you can pass its name to the cmdlet:

The good thing about manipulating rules via PowerShell is that you can filter based on the rule properties. Pipe the output to get-member to see the various properties:

Then you can do stuff like:

To disable and enable rules you have the Disable-InboxRule and Enable-InboxRule cmdlets.

Notice how the cmdlet failed for a client only rule? You can find such rules by using the SupportedByTask property:

Lastly, to mass enable or disable rules do thus (in the examples below I am selecting rules matching certain criteria, but that’s optional):

PowerShell can also be used to create and delete rules. Like thus:

Ctrl+Alt+Del in Remote Deskop

Note to self: to invoke Ctrl+Alt+Del in a remote desktop session press Ctrl+Alt+End. For this and more see this helpful post.

Get-WindowsFeature missing

I always open up PowerShell on $randomcomputer and type Get-WindowsFeature expecting to get a list of Windows features. Sometimes it doesn’t work and then I Google on why that’s the case, forgetting that I’ve been down this route umpteen times. So here’s a post for myself.

The *-WindowsFeature cmdlets are available via the Server Manager module which in turn is either present by default (on servers) or installed via the Remote Server Admin Tools (on clients).

  1. Windows Server 2012: Modules are loaded automatically on demand so the *-WindowsFeature cmdlets are available without any additional steps.
  2. Windows Server 2008 R2: Import the Server Manager module and then the *-WindowsFeature cmdlets can be used.
  3. Windows 8: Install the Server Manager via RSAT. This makes the Server Manager module available for automatic loading and then the *-WindowsFeature cmdlets can be used. Windows 8 also provides (Get|Enable|Disable)-WindowsOptionalFeature cmdlets as part of the DISM module (which is present by default). These provide similar functionality to the *-WindowsFeature cmdlets (doesn’t work on remote computers though!). Add the -Online when using these cmdlets as they can work with the running instance or a mounted Windows image.
  4. Windows 7: Installing the Server Manager via RSAT doesn’t help. It doesn’t include the Server Manager module and so the *-WindowsFeature cmdlets are not available. An alternative is to install the 3rd party Client Manager module which gives the *-ClientFeature cmdlets.

Windows ignores the hosts file

If you find that Windows ignores your hosts file try the following: open the file, select save as, and while saving it change encoding from Unicode (or whatever it is) to ANSI. Be sure to select the file type as “All files” so Notepad doesn’t append a “.txt” to the file name (and double check after saving as sometimes Notepad still appends a “.txt”).

How is this PowerShell related?

I was using Out-File to put something into the hosts file. Windows expects the hosts file to be in ANSI encoding – and seems to ignore it otherwise – but Out-File uses Unicode encoding by default; so the net result is that Windows ignores the resulting hosts file. Even if I removed all the entries in it and made fresh ones for testing, since the file is still in Unicode encoding Windows ignores it. Finally, thanks to a great post I figured a workaround which led to realizing the problem.

Moral of the story: when writing to the hosts file using Out-File specify the encoding as ASCII. Like thus:

And if you are a PowerShell geek, you can avoid the longish method above of changing the hosts file type to ANSI through something like this:

Easy peasy!

Managing BitLocker disks on Server Core

I have a Server Core 2012 that has two BitLocker encrypted disks on it. When I encrypted those disks the server had the full GUI but after I converted to Core there’s obviously no GUI to just double click and be prompted for a password etc. So need to use the command line tools.

There seems to be two ways.

First are the BitLocker command line tools. Manage-bde looks like the most useful command here. Using this one can see the status of all the drives on the machine, lock, unlock, set auto-lock auto-unlock, and also turn on or off BitLocker encryption on a drive.

Typing manage-bde in the command prompt gives you all the options. Each of these options have further switches which you can discover by typing manage-bde <option-name> -?.

To view the status of all drives on the machine:

To unlock an encrypted drive (with drive letter D:) to use with the system:

I use passwords, hence the -pw switch. If you use recovery keys or certificates there are switches for that too. manage-bde prompts for a password and unlocks the drive, mounting it on the specified drive letter.

To set the drive (with drive letter D:) as auto-unlocked:

That’s all. From now on the drive will be automatically unlocked when attached to the system.

The syntax for disabling auto-unlock and locking a drive are pretty obvious from the examples above. The thing to remember is you always specify the manage-bde command followed by a dash switch specifying what you want to do, and after that you specify the drive letter.

There are two other commands: Repair-Bde for repairing corrupted BitLocker encrypted drives and BdeHdCfg for setting up a drive with BitLocker encryption (though it doesn’t seem to be required any more as Manage-Bde includes some of this functionality).

Apart from the BitLocker command line tools you can also manage BitLocker via PowerShell. This is only for Windows 8/ Windows Server 2012 and is available via the BitLocker module (requires RSAT on Windows 8).

To view the available drives on a system and their BitLocker status do:

You can also check the status of a specific drive with the above cmdlet by passing it the drive letter with the -MountPath switch.

To unlock a BitLocker drive (with letter D:) do:

The cmdlet does not prompt for a password. You have to pass it via the -Password switch. You can’t pass the password as plain text either, so have to convert it to a secure string. Use the ConvertTo-SecureString cmdlet for that or just use Read-Host and convert the inputted text to secure string on the fly.

To set auto-unlock on a drive (with letter D:) do:

Similar cmdlets exist for locking and auto-locking drives.

After writing this post I discovered a TechNet article that goes into more detail on the above command line tools and cmdlets. Go check it out.

Format-Table FormatString

In the previous post I wanted to convert some output to percentage figures and resorted to type casting to get it done. Turns out I don’t need to do that. Was reading more about the Format-Table cmdlet and saw that the hash table that supplies calculated properties also supports a key called FormatString which let’s one specify the format of the output.

For a quick overview of FormatString read this MSDN page. You can see various format specifier strings mentioned there. These let you define the format of the output.

Say I want to see the current time and output the remaining minutes to the hour as a percentage.

I can get the current minute thus:

I can use an Expression block to show the balance minutes:

And if I want to format the output such that it’s shown as a percentage (the value multiplied by 100 with a % sign suffixed) I can use FormatString with the “P” format specifier:

This gives the output as a percentage figure with two decimal places (the default). I can modify that by suffixing a number to the “P” specifying the desired number of decimal places. Like for instance:

If I want to round, I can use the “r” specifier. I can do decimal, scientific, fixed point, and so on.

Returning to the code of the previous post, here’s a revised version:

There’s two changes: (1) I don’t type cast the Capacity to [uint32] any more to round it, rather I use the “F0” format specifier which stands for a fixed-point number with 0 decimal digits (i.e. a number like 42.9517669677734 gets converted to 43); (2) I don’t type cast for the free space to percentage either, I just use the “P1” format specifier which stands for a percentage figure with 1 decimal digit (i.e. a number like 48.6%).

Neat!

Get a list of partitions and free space

The Win32_Volume class can be used to list partitions. The default output is very verbose but properties of interest to me are DriveLetter, Label, Capacity, and FreeSpace.

Ok, so let’s tidy this up a bit.

First, let’s get rid of any empty entries like the “System Reserved” partition.

Then let’s convert the capacity figure to GB and express free space as a percentage. This is easily done using Format-Table (I love playing with Format-Table to customize output! I find it tremendously exciting to be able to just format the output using an Expression script block).

Next let’s convert the capacity and free space figures to [uint32] so they get rounded as integers.

And lastly let’s include the label along with the drive letter.

Another useful property from Win32_Volume is DriveType.

This can be used to limit the output to only fixed disks, for instance. The numbers are constants that define the various types of drives (starting from the number 0) so it’s possible to filter by DriveType to limit to fixed disks only.

Just for kicks I’d like to put a % sign after each of the free space figures.

This one’s worth explaining a bit. The change that I made is in the Expression block for free space.

What I did is that I put [uint32]($_.FreeSpace/$_.Capacity*100) – the previous Expression block – within the $() operator so it is evaluated as a sub-expression. In turn I put the $() block within double quotes to type cast the number into a string. And finally I suffix the $() within double quotes with the “%” character so it’s appended to the string.

It’s important to use the $() operator here. Instead, if I had only grouped the code within brackets () what happens is that the expression $_.FreeSpace/$_.Capacity*100 gets evaluated as expected, but the result is not type cast to an integer as [uint32] is treated like a string rather than an operator due to the double quotes casting it that way. So the $() operator is required.

Since the number is a string it gets left-aligned. But that can easily be aligned to the right with the Align key:

See the next post for a different way of doing this.

Get a list of machine names and logged on or active user(s)

The Win32_LoggedOnUser class gives a list of logged on users to a computer. It isn’t just users logged on interactively to the console or Remote Desktop (which is a good thing) but even users connected remotely via shared folders and such (not a good thing).

But just for the curious (and since I fiddled with it today) here’s how you can format the output of this class properly.

You can use PowerShell to get instances of this class via the Get-WMIObject cmdlet thus:

The output has a lot of material, so best to filter by the Antecedent property.

Instead of Format-Table you may use Select-Object or anything else you fancy.

To query remote computers using the -ComputerName switch.

The output isn’t precise. You get a list of local accounts as well, and there are repeat entries.

In my case I want to limit to the domain accounts. Also I want to skip accounts starting and ending with the “x” character (as these are my admin accounts) and any accounts entering with a “$” character (as these are machine accounts). And I want to eliminate the duplicates. So here’s what I did:

Once again, the beauty of PowerShell is that you can glue things together. So, for instance, if I want to get a list of machines in my domain along with the currently logged in users, I can do that.

To get a list of machines in a particular OU I use the following (this requires the ActiveDirectory module to be imported first!):

I already have the code for getting the logged in user on a machine, so all I need to do is pipe the output of the Get-ADComputer to Format-Table and tell it to populate the user column dynamically. Like thus:

The code within the Expression block is similar to the code for querying a single machine, except that I replace the machine name with $_.Name.

The output is a list of machines with the logged in user(s). If there are no logged in users that field is empty {} else it’s a single user name or a list of user names.

Of course, the Win32_LoggedOnUser class isn’t useful if users access each other’s machines via shared folders and such; but in that case it’s just a matter of replacing the Expression block above with code that gives more focussed results. Two great posts on how to list logged in users on a machine are this and this.

I wanted to expand the code above to also include IP addresses too. A PowerShell one-liner to get IP address from a name is the following:

To limit the output to just the IP address do the following:

And to restrict to just IPv4 addresses add the following:

Going by these it’s pretty obvious what we have to do to add an IP address column:

Modifying Junk Email configuration using PowerShell

You can use the (Set|Get)-MailboxJunkEmailConfiguration cmdlets to set or view the Junk Email configuration of a mailbox. For instance:

From the Get-MailboxJunkEmailConfiguration output above I can see Junk Email configuration is Enabled for this mailbox, all email addresses in the Contacts folder of this mailbox are automatically added to the trusted senders list, any emails from the example.com domain are considered trusted senders and delivered to the mailbox, and no domain is considered blocked and hence not delivered to mailbox. Further, the mailbox is not solely relying on these trusted lists – if it were, then only addresses in the trusted list would be allowed through and everything else discarded.

To change these values use the Set-MailboxJunkEmailConfiguration cmdlet. Its syntax is pretty straightforward.

To add entries to the trusted or blocked senders lists one must retrieve the entries first and then append to it. So either do it the longish way like this:

Or do it all in one go like this:

In either case the result is the same:

Of course, goes without saying, the beauty of being able to do something like this via PowerShell rather than manually on Outlook or Outlook Web App is that you can do bulk action. Like for instance:

Easy to see at one shot what the status for each user is.

I can get rid of the OU name in the Identity column via some format-table regexpery.

A variation of the above:

Here I am using regexp to strip out the “rakhesh.com/Planet” bit so I only get the user identity and the OU it is in. I do this by making the + regexp operator non greedy so it doesn’t match all the input text – rather, it stops at the first “/” character. I then put this regexp in brackets to club it together and tell PowerShell to match exactly two instances of this (hence the {2}) so what remains is the bit which I want. Regexps are great when they work and you can make sense of them!

Using FolderID for permission related tasks on mailbox folders

It’s probably obvious – wasn’t for me and the help page doesn’t make it clear – but the (Add|Remove|Get)-MailboxFolderPermission cmdlets don’t need a human readable path to the folder to do their deeds.

If you look at the help page for the Get-MailboxFolderPermission cmdlet it says the Identity parameter takes input of the format <SMTP Address or Alias of the mailbox>:<Folder path> and gives as an example “john@contoso.com:Calendar”. This gave me the impression that I must pass the Folder path as a path to the folder and that there’s no other way of doing this. But that’s not the case. The Folder path can be a FolderID too.

I haven’t managed to find much info on what a FolderID is, but if you want to find it for your own folders the easiest way is a cmdlet such as this:

As far as I can tell slashes apart from the first one don’t seem to mean much (they don’t denote sub-folders at least). And there’s no easy way of identifying whether a folder is a sub-folder of another just by looking at the FolderID.

I find it much better using FolderID to assign mailbox folder permissions recursively rather than using Folder path.

With Folder paths I do something like this usually:

The bit to note is line 4 where I do a bit of regexpery to convert the output from Get-MailboxFolderStatistics to the format required by Get-MailboxFolderPermission. The former returns Folder paths of the format /path/to/folder while the latter requires them to be <User>:<Folder path>.

If I don’t use Folder paths, I can replace the above code with this:

Much simpler looking code. And it also has a not so obvious advantage in that the previous code has a bug if the folder names contain the “/” character. Have a look at the following:

Notice the folders with a question mark in them? These appear because the folder name contains the “/” character and since that’s used to separate folder paths the cmdlet replaces them with a “?” character. To get the code to work you’ll also have to replace the “?” character with a “/”. (The character is actually not a question mark, it is only displayed thus because the console cannot display what it properly. The character is actually ASCII/UTF-8 code 63743 (hex code 0xF8FF) and you can match it with a regexp such as "uF8FF").

If you use FolderID instead, you don’t have to worry about such fringe cases.

Dot star replace matches twice

Have a look at this:

Works as expected. It matches “bc” in the input text and replaces the output with “ff” followed by whatever was matched (in this case “bc”).

The second example makes things clearer. It’s obvious in retrospect but easy to forget. We are not matching for the words “bc”, we are actually matching for a pattern “abc”: the text “bc” preceded by an “a”. If such a pattern is found, that matched pattern is replaced with the words “ff” followed by part of the matched pattern (in this case “bc”).

The third example simply drives home the point that the pattern can be anywhere in the input and that multiple instances are matched & replaced.

Another one:

What’s going wrong here? I expected the entire input to be replaced with one word – “blah”. Why do I have it twice then? This happens even if I do variations of the above thus:

It looks like the pattern matching-replacing is taking place twice. Lines 3 & 4 make it clearer above. The first time “abc” is replaced with “blah[abc]” as expected, and then a matching-replacing seems to occur again on that same input text adding “blah[]” to “blah[abc]”. No matching seems to take place however, or the matched pattern is empty, and that’s why there’s no text within the square brackets.

Hmm. So maybe that’s the problem! The matched pattern is empty – which is acceptable as my regexp pattern is .* and that matches empty text too. If I replace .* with .+ (at least one character must match) does that work as expected?

Sweet, it does!

Moral of the story: be careful using the .* pattern – apart from matching everything in the input, it will also match the emptiness that remains.

Obfuscate email addresses with PowerShell

Came across the following piece of code in someone’s signature today.

It’s a geeky way of obfuscating your email address. If you copy paste that bit of code into a PowerShell window and press ENTER, you get an email address.

I’d like to make something similar. Especially since it just coincides with what I was playing around with today morning. But before that I’d also like to understand what’s going on above. So let’s break this one-liner and peek behind the curtain.

Understanding

First things first: if I remove the [string] type cast and run the code I get a bunch of characters – one on each line. So what the type cast does is convert these characters to a one-liner string.

Next, I piped the email address that was output to the Measure-Object cmdlet with the -Character switch. That gave me the number of characters in the address: 34. This coincides with the next bit of the code – 0..33 – which is shorthand for generating the numbers 0 to 33 (34 numbers). So looks like that bit is responsible for driving something to generate each character of the address.

Great. What’s happening next?

The code generates 34 numbers. For each of these numbers it does something.

First a sub-string is extracted from that long series of numbers. This long series of numbers is stored as a string (hence the double-quotes). From this a sub-string of 2 characters is extracted. The starting point of extraction jumps forward by 2 each time (position 0, then 2, then 4, 6, 10, and so on up to 66) – breaking the long string into a series of 2 digit numbers.

Here’s the relevant code:

This generates numbers 68,65,52, … 70.

To each of these the code adds the number 46. So the numbers actually output are 114,111,98, … 116. Each of these is then converted to a character – which is what the [char][int] bit does. And the rest we already know – these characters are converted to one long string showing the email address. (There’s one gotcha though, in that the string has the characters separated by a space. Which is why we have a -replace " " tacked on the end – this simply replaces are spaces with nothing, combining all the characters into one word).

Genius! (At least for a PowerShell child like me).

Creating

Okay. So how do I create something similar?

First things first: if I were to just convert an email address to ASCII/ UTF-8 codes what do I get?

I could join these numbers, with dots perhaps, to form a long string.

If I wanted to get my address from such a string I’d do something like this:

That outputs the numbers, I can just convert them back to an address.

Great. So that’s something I could paste in a signature somewhere and anyone running that code will get an email address.

I got side tracked though. What would I do to get something similar to the code the user created? Backtracking:

I have a series of numbers. They are of varying length so I can’t just club them together and hope to extract. I need to get them all to a uniform length, so an easy idea would be to just subtract them all by some number to make them 2 digits each. The number has to be larger than 46 – as that’s the smallest number – so why not just use 46 as the number to subtract from all?

So now all I need to do to reverse the process is (a) split the long number into chunks of two, (b) add 46 to each, (c) convert to a char, and (d) convert the array of characters into a string.

It worked! Sort of. Something messed up though. And the email address is mostly generated but for the last 4 characters. It looks like the string is falling short for the Substring method.

A few minutes of head scratching later, I realize the problem. When I converted all the numbers to two digits, one of them was one digit – the number 46 that got subtracted by itself. This was resulting in a string that fell short. Not to worry, all I had to do was pad a 0 to that number and then it will work.

Automating

Can I automate this process? One where any string I give is converted to code that will generate the string when run.

A proper generic code would take a bit more effort and time, but since I am mainly concerned with email addresses I can take some short cuts. If I were going to build a generic code I’d (1) need a way to find the smallest ASCII/ UTF-8 code in the set entered, (2) while subtracting that number from every number I’d have to ensure any single digit number is accordingly padded, and (3) I’d also have to be concerned with any text that may have an ASCII/ UTF-8 code which won’t reduce to two digits when I do the subtraction above.

First thing to notice is that the number that we are subtracting – in my example above, as well as the code I started off with – is 46. Which turns out to be the dot in the email address. Next thing to notice is that letters A-Z and a-z have codes 65-90 and 97-122. Similarly the numbers 0-9 have codes 48-57. So by limiting myself to this set of characters I can be sure that the lowest number will always be 46, and that I can subtract it from any of the expected characters and expect a single or double digit result.

But why not got a teeny weeny step further. Checking WikiPedia for the syntax of valid email address I see that the lowest ASCII/ UTF-8 code for an allowed character is 32 (the character being “) and the highest allowed ASCII/ UTF-8 code for an allowed character is 126 (the character being ~). So the code I generate will be similar to the code I started this post with, except that I’ll be adding 32 instead of 46. And so the code I create to generate this code will output 32.

So without much ado here’s the code:

Of course, replace “abc@example.com” with the email address you’d like to obfuscate.

For the curious, here’s an explanation of my code (the bit after the first pipeline).

Line 1 outputs the part of the final code that starts with a [string] and ends at the beginning of the series of digits (just before the left bracket).

Line 3 outputs the part of the final code that starts where the digits end (just after the right bracket) and goes all the way up to -replace " ".

These two lines are pretty straight-forward. Only point to note is I escape the $ symbols with a backtick (`) so they don’t get interpreted as variables. And I get the length of the input text via $_.Length (this is why I put the whole block within a ForEach-Object loop as that’s the only way to make $_ variable available). Also, I put the expression as $($_.Length-1) because if I don’t do that PowerShell expands $_ into a string and then tacks “.Length-1” to it as another string.

Line 2 is a variant of what I created earlier. It converts the input string into an array of characters, cast into an array of integers, and pipes these into another ForEach-Object loop that subtracts the number 32 from each and if the result is less than 9 (i.e. single digit) pads a “0” before outputting the number.

And that’s it!

Getting the ASCII/ UTF-8 value of a string

To display the character associated with an ASCII/ UTF-8 code do the following:

Thanks to this PowerTip.

Now how about the reverse? Can I get the ASCII/ UTF-8 code of a character.

No luck. But I think am the right track.

The error InvalidCastFromStringToInteger is what tells me PowerShell is trying to cast from a [string] to [int] – which is not what I want and will obviously fail. I want to cast from a [char] to [int] so let’s be explicit about that.

Good, that works!

Now how about getting the ASCII/ UTF-8 of a string. Can I do that?

As expected, you can’t just pass two characters and hope it works!

What I need is an array of [char] elements. Which I can then type cast to an array of [int] elements.

First let’s look whether there’s any method available to convert a string to an array of characters?

Looks like there is. Does the following work?

No, but that gives me a hint on the solution. The output of the ToCharArray() method is of the data type System.Char[] whereas [char] is shorthand for the System.Char data type.

So maybe [char[]] is what I need? Does such a data type exist?

Sure enough it does!

So let’s try the following:

I don’t need the ToCharArray() method either as if I just type cast a string to an array of characters the method is invoked implicitly. Sweet!

Armed with this info I try type casting the string to an array of integers to get their ASCII/ UTF-8 values:

Nice!

Can I make this better? As in, say I had a longish string; currently the above snippet just gives a bunch of codes and that’s not very helpful if I want to see the code of a particular letter. Can I get the output such that it shows each character followed by it’s ASCII/ UTF-8 code? Something like this:

D’oh! Doesn’t help. But I am on the right track, and I know what to do. You see, within double quotes the [int] is not evaluated (thanks to this Hey, Scripting Guy! post) and so I have to force evaluation through any one of the methods mentioned in that post. I prefer the VBScript approach, so here goes:

Bingo!

Convert a bunch of VHD files to VHDX

Here’s how to convert a bunch of VHD files in a directory to VHDX. The actual conversion process uses the Convert-VHD cmdlet so you need to be on Windows 8 or Windows Server 2012.

I am storing all the VHDX files at “C:\Hyper-V\Virtual Hard Disks” which is why I put the path there. If you’d rather put the VHDX files in the same location as the VHD files use the following instead:

Note to self: If you convert the objects returned by Get-Item to string (by doing $_) you get the file name with the full path (this is equivalent to $_.FullName). If you want just the file name, use $_.Name. If you want the file name only, without the extension, use $_.BaseName; and if you want the extension only but not the file name use $_.Extension.

Extract a variable from a bunch of files

I have a couple of TXT files. They contain some text, along with a line like this:

The version number keeps changing.

I want to list all these files along with the value of $version in that file. Here’s how:

The result is something like this:

It’s pretty straight-forward what I am doing. I use Get-Item to get all the TXT files. Pipe it to a ForEach-Object loop. Within that I use the Get-Content cmdlet to get the contents of the file. I could have stored the contents in a variable and then done the remaining bits, instead I send it directly to the -match operator to get out lines that contain the pattern I am looking for. After this I replace the line to extract the numbers between double-quotes and store that in a variable. Finally I output the file name and the version (only if a version was found).

The two patterns pattern took me a while to figure out so I’ll delve a bit more into it here. I use a similar pattern for the -match and -replace operators so my comments here apply to both. The patterns are pretty straight-forward but for the fact that I need to match symbols such as dollar ($) and double-quotes (“). That in itself is not a hassle as I can escape them and match as $ or ” but that didn’t work in my case. I kept getting errors like this:

Why does this happen? Thanks to a Stack Overflow post I got the answer. What happens is that although I escape the $ as $ within the regular expression, since $ is a special character for PowerShell (variable names) it clubs the $ with the word “version” and treats it as a variable $version. And since this variable isn’t defined, it expands to an empty string which PowerShell passes to the -match operator as the pattern “^”. This pattern doesn’t make sense and so we get the error above.

Workaround is to double escape the $ symbol. As `$. If we do that I get the following error:

Not sure why I get an error here. The period character is an operator in PowerShell, and since the error points to that something roundabouts the period operator is confusing PowerShell. Ideally the whole pattern ^`$version = ".*" should be passed to the -match operator, but PowerShell only seems to be passing ^`$version = and using the period as a deference operator to that string. Which of course will not work because the left hand side is a string and not an object, but also because the right hand side *"$" is not a property name. So again it looks to be similar to the earlier error. The double quotes are being usurped by PowerShell first, before passing to the -match operator, and so they too need to be double escaped.

Hence I use the pattern ^`$version.* for the -match operator and the pattern ^`$version = `"(.+)`"$ for the -replace operator. Double-escaping the $ and ” characters.