Tag Archives: Windows PowerShell

Bye-Yee ISE – Hey Yo VS Code

It is official — as official as it is going to get anyway — but I am in the final six-month stretch of my eight-year consecutive run of writing about PowerShell. Not that I will be done after eight years, but I have been at this awhile. What is also official is that I have written the dumbest post title yet. I am going to go with it though.

Today I had an interesting “conversation” on a post in the PowerShell Subreddit. The full link, if you are interested, is included at the bottom of this post. But really, the important parts are all included here.

The post was about someone using Write-Host that should have been using Write-Output. That was not where I took the conversation, however. Instead, it became a discussion about moving away from the ISE to Visual Studio Code. I think that this is an important move, and so I thought I would include it all here because I think I made some good points, and maybe because I am after that 416 post count by June 2022. More the first, though.


Me: “Beyond that, OP, it is time to move away from the ISE, and probably Widows PowerShell too. 2¢


Not me, or the OP: “Nah, ISE4LYFE

Me: “[SMH] Think of your résumé: ISE vs. VS Code. Speaking of your résumé, did you do create that in Notepad and print it on a dot matrix? 😉 I loved the ISE once too, but I think it’s time…


Not me, or the OP: “I mean, I’m not old enough to have used dot matrix (well, kinda, 30)

My main issue with VSCode vs ISE is that it’s a UI mess from as soon as you install/open. Vs ISE, it’s just notepad ontop of powershell.

ISE is just easier for me for the quick and simple stuff, where i just bust out some script for a minute or two.

Granted, running the code-server is really nice


Me: “Oh, I get it. VS Code takes some time. It took me some to finally give up the ISE in full. And, I’m the same guy that paid attention to the PowerShell launch in 2006. One person, around that time (that I believe was associated with Microsoft), said that if you’re using VBScript, then continue to use it. That it is still going to be there, as in, in the operating system. That’s all it took for me to not pay attention to PowerShell! I bet the ISE is still going to be there too.

Cut to six years later, in 2012, and I finally let go of VBScript, which I loved, and forced myself to use PowerShell, which I now love. You’d have to pay me a large amount of money to even look at VBScript now. While I can’t say the same thing about the ISE, I’d miss VS Code an awful lot if the ISE is all I had. While it took some time and frustration and wanting to go back, I pushed on!

Speaking of, “if the ISE is all I had,” I recently wrote an AWS CloudFormation template to build out a vanilla Windows Server, server. It was beyond frustrating to build a new instance and only have the ISE. As a part of the CloudFormation, I installed PowerShell 7, installed VS Code, and installed the PowerShell VS Code extension. Again, I once loved the ISE.

Here is the PowerShell Subreddit post.

PowerShell and the LAPS Windows PowerShell Module

There’s a difference between Windows PowerShell and just PowerShell. You know that, right? You know what the difference is, yeah? If not, then do know that Jeff Hicks wrote a great blog post about it recently titled “What the Shell is Happening?” I’m going to assume you’ve read this as I go along, so you might just do that quickly if you haven’t already.

Windows PowerShell and PowerShell really are two different things. We’re moving into a territory where you’ll be able to know how much someone knows about Windows PowerShell/PowerShell based on how they are discussed. It’s going to be like PowerShell quoting rules: You can tell a good deal about someone’s Windows PowerShell/PowerShell knowledge based on when they do and don’t use single and double-quotes.

I was recently looking at some documentation when I happened upon the LAPS Windows PowerShell module. If you’re looking for a strangely named module then look no further; it’s AdmPwd.PS. Seriously. How about just calling it LAPS!? Up until that point in the documentation review I was doing, everything I tried was working in PowerShell — commands from the Hyper-V module, commands from the ActiveDirectory module (read the Edit section at the bottom of this post when done reading this post), and all of the we-ship-these-with-the-product Microsoft modules and commands.

The LAPS module — we’ll just call it that — is stored not only as a Windows PowerShell module, but it’s stored in the C:\Windows folder. That’s C:\Windows\System32\WindowsPowerShell\v1.0\Modules to be exact. While tab-completion didn’t work on the module’s name, the Import-Module cmdlet did work, but not without a warning.

The warning message above has to do with how PSRemoting sessions work. First off, a PSRemoting session is usually done from one computer to another. In this instance, however, it’s doing a PSRemoting session from and to the same computer: mine. In that remoting session, it’s loading up Windows PowerShell (powershell.exe [not pwsh.exe]). As can be seen below, running Get-PSSession will provide information about the Windows PowerShell (or WinPSCompatSession) PSRemoting session. Notice the -1 value for the IdleTimeout. Without any reading and research, I’m going to say that it means the session doesn’t timeout. I left mine up for an hour or so, and sure enough, the LAPS commands continued to work without having to recreate a PSRemoting session.

In any PSRemoting session, whether to the same computer or another, we get deserialized objects as the results. That means that the final results, that end up on the source computer from the destination computer, do not consist of live PowerShell objects. I’m sure you can read more about it, but the result/output is serialized into XML, sent across the wire from one computer to the other, and then deserialized. This has to do with how to quickly move data from one machine to another. In our case, even though it’s the same computer, it’s still making use of the network card and serialization/deserialization. It’s still doing this even if your PSRemoting session is to and from the same computer.

But there’s another way we can use this module. As mentioned in our warning message, Import-Module includes a SkipEditionCheck switch parameter. According to the documentation, this forces the command to skip evaluating CompatiblePSEditions in a module’s manifest file. This is a key within the file that indicates whether or not a module can be used for Windows PowerShell, which uses the term “Desktop” if it can, or PowerShell, which uses the term “Core” if it can. If a module was designed to work with both Windows PowerShell and PowerShell, it would include both terms. The LAPS module was written before CompatiblePSEditions was added to the module manifest files (.psd1 files).

When this parameter is used, it doesn’t look for CompatiblePSEditions, as stated, and appears to load the LAPS module properly. Well, properly enough that I was able to test the Get-AdmPwdPassword command. Before we see some proof supporting that claim, let’s take a look at some very important information from the Import-Module documentation.

It turns out it’s very likely this module and its related commands wouldn’t have worked using the SkipEditionCheck parameter, but so far they do. Oh, I didn’t mention this, but in the documentation for Import-Module and the SkipEditionCheck parameter, it does mention the path where the LAPS module is located, “Allows loading a module from the “$($env:windir)\System32\WindowsPowerShell\v1.0\Modules” module directory into PowerShell Core when that module does not specify Core in the CompatiblePSEditions manifest field.” So, a part of how this works is also due to the module’s location in the file system. Now, here’s our successful invocation of the Get-AdmPwdPassword command.

By using this method — and we’re lucky we can it seems — we skip creating a PSRemoting session from/to the same computer. Therefore, we aren’t forced to work with deserialized objects. Typically, it’s fine if you do, but with the LAPS module and PowerShell, it’s not a requirement. Take a look at the TypeName value above. That’s a clear indication that we are working with live PowerShell objects. We didn’t see this earlier in our PSRemoting session, but if we had, it would’ve said, “Deserialized.AdmPwd.PSTypes.PasswordInfo.”

Edit: A few days have passed since I first published this post. I didn’t know it then, but I do now! The ActiveDirectory module in PowerShell also creates a PSRemoting session! It looks like it’s running in PowerShell, but it’s really running in Windows PowerShell “behind the scenes.” I’ve had no problems dealing with a deserialized objects, by the way. This is because any piping I might do happens on the remote computer (my computer, but yeah), before the serialization/deserialization process.

Prestaging Modules for PowerShell, Windows PowerShell

I recently wrote this to a colleague: “Finally, if you’re going to peer into the module manifest file (.psd1), you might also check ‘CompatiblePSEditions.’ If it includes both the ‘Core’ and ‘Desktop’ values, then you might consider installing modules in both $env:USERPROFILE\Documents\WindowsPowerShell\Modules and $env:USERPROFILE\Documents\PowerShell\Modules.”

He’s using SCCM, Software Center, and a Windows PowerShell form to install modules. My thought was to install it for both PowerShell and Windows PowerShell, regardless of which they were using and in reference to the manifest file. This would ensure a module was already in place provided the user switched between PowerShell and Windows PowerShell or moved from one to another one day. Not a bad idea, I suppose. If the duplicated module is never used, it’s okay as the disk space used would likely never really be noticed. Maybe one day, PowerShell will include this option itself. You install a module in PowerShell and include some yet-to-be-determined switch parameter, and boom, it’s ready in both locations.

This got me thinking. How would one write the code to do the conditional logic here? While it wasn’t my project, I couldn’t help myself from determining how I might write it. And, when it was done — and this has happened before — I wasn’t sure what to do with it. Enter, this blog post; its new home, or final resting place.

I wrote this to check four different modules on my computer. They were: (1) AWS.Tools.Common, (2) AWSLambdaPSCore, (3) ISE, and (4) ExchangeOnlineManagement. Each of these four contained a different value in the psd1’s CompatiblePSEditions. In the same order as above, there were (1) Core and Desktop, (2) Only Core, (3) Only Desktop, and (4) Neither Core nor Desktop.

Here’s what I wrote; we’ll discuss it further below. It was saved into a file named ModuleDeploy.ps1.

# Both Core AND Desktop.
$Path01 = "$env:USERPROFILE\Documents\PowerShell\Modules\AWS.Tools.Common\4.0.5.0\AWS.Tools.Common.psd1"

# ONLY Core.
$Path02 = "$env:USERPROFILE\Documents\PowerShell\Modules\AWSLambdaPSCore\2.0.0.0\AWSLambdaPSCore.psd1"

# ONLY Desktop.
$Path03 = "$env:SystemRoot\system32\WindowsPowerShell\v1.0\Modules\ISE\ISE.psd1"

# Neither Core NOR Desktop.
$Path04 = "$env:USERPROFILE\Documents\PowerShell\Modules\ExchangeOnlineManagement\1.0.1\ExchangeOnlineManagement.psd1" 

$Paths = $Path01,$Path02,$Path03,$Path04
Foreach ($Path in $Paths) {
	"`r`n||| Module: $(Split-Path -Path $Path -Leaf) |||"
	$Psd1File = Import-PowerShellDataFile -Path $Path

	Switch ($Psd1File.CompatiblePSEditions) {
		{$_ -contains 'Desktop'} {
			Write-Output -InputObject 'Copy to WindowsPowerShell directory.'
		} # End Condition.

		{$_ -contains 'Core'} {
			Write-Output -InputObject 'Copy to PowerShell directory.'
		} # End Condition.

		default {
			If ($PSVersionTable.PSEdition -eq 'Desktop' -or $null -eq $PSVersionTable.PSEdition) {'Copy to WindowsPowerShell directory (default).'
			} ElseIf ($PSVersionTable.PSEdition -eq 'Core') {'Copy to PowerShell directory (default).'} # End If-ElseIf.
		} # End Condition.
	} # End Switch.
} # End Foreach.

Let’s quickly cover what’s happening in the above code. Then we’ll view the below results for the execution in both PowerShell and Windows PowerShell. Showing both versions may clarify the reason for the If statement inside the above final/default Switch condition. We’re already jumping ahead.

First, we created four different path variables for four specific module manifest files (.psd1 files). The first one is to a module that includes both “Core” and “Desktop” in CompatiblePSEdition. The second only has “Core,” the third only has “Desktop,” and the final one has neither/nothing. This was mentioned earlier. These four variables and their assigned values are combined into an array, which we then iterate over inside of a Foreach loop.

During each iteration, we run through a Switch statement that indicates where each module would be copied (if we were actually copying the modules). Based on the value(s) in CompatiblePSEdition for each of the files, the first module would be copied to both the PowerShell and WindowsPowerShell modules, the second, only to PowerShell modules, and the third, only to WindowsPowerShell modules. The final copy is dependent on whether or not we’re using PowerShell or Windows PowerShell. Take a look at the results.

PowerShell 7.0.1

[PS7.0.1] C:\> . "$env:TEMP\ModuleDeploy.ps1"

||| Module: AWS.Tools.Common.psd1 |||
Copy to PowerShell directory.
Copy to WindowsPowerShell directory.

||| Module: AWSLambdaPSCore.psd1 |||
Copy to PowerShell directory.

||| Module: ISE.psd1 |||
Copy to WindowsPowerShell directory.

||| Module: ExchangeOnlineManagement.psd1 |||
Copy to PowerShell directory (default).

WindowsPowerShell 5.1

[PS5.1.18362.752] C:\> . "$env:TEMP\ModuleDeploy.ps1"

||| Module: AWS.Tools.Common.psd1 |||
Copy to PowerShell directory.
Copy to WindowsPowerShell directory.

||| Module: AWSLambdaPSCore.psd1 |||
Copy to PowerShell directory.

||| Module: ISE.psd1 |||
Copy to WindowsPowerShell directory.

||| Module: ExchangeOnlineManagement.psd1 |||
Copy to WindowsPowerShell directory (default).

That’s it. Just some code that needed a home. I’m fully aware that there’s better ways this could be done, and that there would be some copy duplication if this were dropped into production the way it is. Additionally, if a directory didn’t yet exist, it might have to be created. Still, it needed a home (as is), and I didn’t think about much more than the conditional logic involved to determine where a module would need to be deployed.

Run One from the Other

It wasn’t but a few days ago that I saw something that piqued my interest. It was an example of running a command against PowerShell Core (pwsh.exe) from Windows PowerShell (powershell.exe), or maybe it was the other way around? Whichever, I’ve included an image that shows this working in both directions. That’s to say it’s an example of running the same command in both versions of PowerShell, from each version of PowerShell.

I do suspect the picture will help explain things, if that last paragraph didn’t.

In the above image, we can see that we’ve run a command against both Windows PowerShell (5.1) and PowerShell Core (6.1). In both consoles, we’ve run the same command; it’s included below. Its goal was to return the version of PowerShell, for both versions of PowerShell.

'powershell.exe','pwsh.exe' | ForEach-Object {
    & $_ -Command {$PSVersionTable.PSVersion}
}

Our results are identical; we knew, we were running 5.1 and 6.1. Neat, right!? Keep this trick in your back pocket, as I do suspect it may be helpful for one of us, one day. Maybe it won’t have anything to do with obtaining the version, of a version of PowerShell, and instead someone will find another use.

Here’s a start. The thought I had was, can I run an Active Directory command in Windows PowerShell (powershell.exe) from PowerShell Core (pwsh.exe)? You bet I can.

In the below example, I’ve returned my GivenName from Active Directory using Windows PowerShell, from PowerShell Core. That could be potentially helpful for someone in some yet-to-be-thought-of project.

Especially in the case of Active Directory, it’s important to remember that each time a command is run, it must import the Active Directory module. Consider that each command is spinning up a new powershell.exe process. For all we know, this may be the reason why the WindowsCompatibility module uses PowerShell sessions, and not PowerShell processes.

In this next example, we issue a Windows PowerShell only command that’s tied to a builtin Microsoft Windows PowerShell model. We issue the Get-LocalUser cmdlet out of the Microsoft.PowerShell.LocalAccounts module. While it’s still importing a module into a powershell.exe process, it loads quicker than the ActiveDirectory PowerShell module did.

I’m going to need to call this a night here soon, but I keep finding more things to try. Like this example. There’s two commands. The first one runs Windows PowerShell, which runs PowerShell Core to determine the version of PowerShell Core.

The second one runs Windows PowerShell, which runs PowerShell Core, which runs Windows PowerShell to determine the version of Windows PowerShell. The image for these examples might be easier to understand, too.

Alright, that’s it for now. Perhaps I’ll come up with some other ideas to try another day. I need to put this post, and me, to bed.