(crazy) Tips: editing scripts

I’ve made a typo when invoking a script and was surprised by the result.
I decided to investigate and tried to understand what just happened.
Without further suspense, I did actually append a dot at the end of my script.

Whoaaa, notepad opened the script.
I didn’t get an error saying this file path doesn’t exist.
That was such a fast way to open my brave old notepad that this typo might even become my new way of editing scripts ๐Ÿ˜›
I mean, instead of typing notepad c:\test.ps1, I’ll from now on type c:\test.ps1.

To understand what happened, I first traced side by side my surprising command and the one I was supposed to run:

Trace-Command -Name * -Expression { C:\test.ps1 } -PSHost
Trace-Command -Name * -Expression { C:\test.ps1. } -PSHost

At the beginning, everything looks similar

But at some point, it starts doing something else (as you can see below).
It starts examining my PATHEXT environment variable.

To better highlight what I happened, I did:

Trace-Command -Name CommandDiscovery -Expression { C:\test.ps1 } -PSHost
Trace-Command -Name CommandDiscovery -Expression { C:\test.ps1. } -PSHost

In the first case, the CommandDiscovery correctly found that c:\test.ps1 is a script and invoked it.

In the second case, the CommandDiscovery found that c:\test.ps1. is an application and started looking for an application that can handle this extension.

If it finds a MRU list under HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\OpenWithList (if you’ve ever double-clicked on .ps1 file), it will use it.

If it’s not the case, it starts looking at this registry key HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\OpenWithProgids

and it should point to:

But when it doesn’t find the MRU list or the OpenWithProgids

It writes the OpenWithProgids value in the user hive and set it to Microsoft.PowerShellScript.1

(I’ll over simplify it) It then starts looking for any application listed under this key HKEY_LOCAL_MACHINE\SOFTWARE\RegisteredApplications to find out if .ps1 is listed in their “capabilities”

Then under the same key but under the user hive: HKEY_CURRENT_USER\Software\RegisteredApplications

It then reaches back again what’s in the machine hive to “open” what it detected as a “Microsoft.PowerShellScript.1”

and uses notepad

Advertisements

Tips: How to avoid redundant code

While working on a module that has a huge memory workload, I wanted to avoid redundant code and used the following nice tips Mike F. Robbins demonstrated in this blog post
First try the following in a console:

$test = 'Heavy task'
($test = 'Heavy task')

Do you see the difference?

Having parentheses around the variable statement also outputs the result in the console.

Isn’t it very nice?

Why there is such a difference?

Having parentheses will make the interpreter parse the simple pipeline/expression (what’s inside the parentheses) in expression mode and output the result of this expression. It’s notation is:

(<expression>)

How do I know this?
Well, I just took the example Bruce Payette gave in the “Statement termination” paragraph in his awesome book “PowerShell in Action” and the error message revealed it ๐Ÿ™‚

Let me also quote what’s written in the “grouping /subexpressions and array expressions” paragraph:

Parantheses group expression operations and may contain either a simple expression or a simple pipeline

The notation can be either

(<expression>)

or

(<pipeline>)

Now, consider the if/else statement for a few seconds that has the following notation:

if (<pipeline>) { 
 <statementlist>
} elseif (<pipeline>) {
 <statementlist>
} else {
 <statementlist>
}

It also means that you can do both the variable assignment and test if anything assigned exists inside the if pipeline/expression. It will evaluate to $true if the output exists and to $false if it doesn’t. As far as I remember, some participants in the scripting games were also using this approach and Don Jones talked about it a few years ago (if you’ve a link please share it, I’ll add it).

When you process only once the heavy workload, capture its result into the variable and immediately test if it exists, it also allows to write less code and have a slightly faster execution in memory.
Here’s an example of what I mean:

if ($test = 'do my heavy task/function/cmdlet') {
 $test | Do-Something 
} else {
 Do-SomethingElse
}

Using the above syntax, you can actually avoid writing more code.
Please, let me repeat that there’s nothing wrong in doing the following:

# assign once but outside of the if/else statement
$test = 'do my heavy task/function/cmdlet'
if ($test) {
 $test | Do-Something 
} else {
 Do-SomethingElse
}

But, you definitely should avoid running twice the heavy memory workload if you do:

if ('do my heavy task/function/cmdlet') {
 $test =  'do my heavy task/function/cmdlet' 
 $test | Do-Something 
} else {
 Do-SomethingElse
}

Let’s measure how fast each of these pieces of code run.
I haven’t found better examples yet but you can try these examples.
The first and second examples run almost at the same speed.

# Example1:
# Both variable assignment and test 
# inside the if/else
Measure-Command {
 if ($test = 1..10000 | % { $_ * 2 }) {
  'ok'
 } else {
  'nok'
 }
}

# Example2:
# Variable assignment outside
# and test inside the if/else
Measure-Command {
 $test = 1..10000 | % { $_ * 2 }
 if ($test) {
  'ok'
 } else {
  'nok'
 }
}

# Example3:
# Test an expression
# Late variable assignment
# Runs twice, so avoid it
Measure-Command {
 if (1..10000 | % { $_ * 2 }) {
  $test = 1..10000 | % { $_ * 2 }
  'ok'
 } else {
  'nok'
 }
}

Windows 10 Update and Privacy Settings

I had the displeasure to start the control panel on my Windows 10 (1607) this Sunday morning and find an entry I didn’t know about and that I didn’t install myself otherwise I’d remember having done so.
Here’s what I saw:

Being a sysadmin dealing with security, I immediately thought “OMG! My children running as standard users have been able to install a software that was able to do a privilege escalation despite the harden configuration I set up”.

In less than 2 seconds, knowing exactly what registry keys have been queried to populate these entries in the control panel, I realized my kids who have been somehow trained about social engineering tactics,… wouldn’t have fallen in this basic trap and install something called “Windows 10 Update and Privacy Settings”.
Sure, my 10 years old son was able to identify the following malicious tactics described in this article “Breaking down a notably sophisticated tech support scam M.O.” a few weeks ago before this article was published.

Mystery solved. As you can see above, there’s URLInfoAbout that points to http://support.microsoft.com/kb/4013214 and it’s not a fake.

Is it legitimate?
Knowing also how to find other footprints of this installation in the registry, I was able to identify where the msi file is stored on the disk.

Using the local package path, I can check its digital signature like this:

What the irony! Something legitimate called “Windows 10 Update and Privacy Settings” was installed automatically via Windows Update without prompting me to do so. I could have been prompted like any license agreement with a message saying: “Hey! We are preparing 1703/creator update deployments and need you to pay attention to this update and install it before we can run the upgrade.”

First release of MVP PowerShell module

My friend and fellow CDM MVP Franรงois-Xavier Cat asked early February if anybody wanted to work on the newly announced MVP API.

We started working on this project and @LazyWinAdm released the first version on the PowerShell gallery on this page: https://www.powershellgallery.com/packages/MVP

I’m glad I could help and collaborate with FX on this project ๐Ÿ˜€

If you want to give it a try, you can start to read the awesome detailed blog post FX wrote about the module, the API and how to get started:
https://lazywinadmin.github.io/powershell/2017/05/MVP_Module.html

Of course, if you encounter any issue and want to provide some feedback, suggestions, and/or pull requests, you can using the following github repository:
https://github.com/lazywinadmin/MVP

#PowerShell and its community rock ๐Ÿ˜Ž

April 2017 Stop-Computer issue

If you’ve updated your Windows computer and use the Stop-Computer cmdlet, you may have noticed that it doesn’t work anymore as it used to.

Instead you get an error saying:
Stop-Computer : Privilege not held.

This was quickly reported to this reddit thread and a PowerShell team member, Steve Lee, quickly answered that the culprit is actually a .Net update introduced in April.

As you can see, he also immediately provided a workaround. You have to use the -AsJob switch that will create a WMIJob

I must add that WMI is also impacted by this regression.
Using the brave old Win32Shutdown WMI method has the problem as the Stop-Computer cmdlet.

Microsoft acknowledged the problem and provided another workaround on this page

I must also add that the problem isn’t only present only on Windows 10, any Windows that had this .Net update is currently affected.