PowerShell PSObjects – Think outside the box!

Working with PSObjects is essential to anyone using PowerShell. Especially, when there’s a need to extend the object to provide useful information.

Remember, the best way to get familiar with the PowerShell object(‘s)… (AKA PSOBject, .NET Object) is to use the ‘Get-Member’ Cmdlet.

Learn-By-Example

Creating custom properties on an existing PSObject:

1. Add a row property using Custom Expression: @{ label = ‘xxcolnamexx’; expression = { “xxxany code logic here..xxx” }; }

2. Add two new columns using the Add-Member command: “SystemName” and UserName – using existing PowerShell session environment variables: $env:COMPUTERNAME, and $env:USERNAME.

Building our Sample PSObject

Let’s create a PSObject using the following *command:

$myWindowsfilesList = Get-ChildItem C:\Windows -Recurse -ErrorVariable flerr;
$myWindowsfilesList.count

*Note: Please ignore the errors and we’ll get back to it later. Errors are saved in the $flerr variable for later viewing.

## – List a small range of rows without the “Row” Property:
$myWindowsfilesList[0..5]
$myWindowsfilesList[1000..1005]

Now, with the help of PowerShell Custom Expressions, we’ll be providing a ‘row’ property to the existing output object. The new ‘Row’ property will be included in the “Select-Object” command and at the end of the process, will be saved as a new PSObject: “$myNewWindowsfilesList”.

Remember to initialize a global variable counter used for the row property.

To build the Custom Expression for the ‘Row’ property, see the line below:

@{ l = 'row'; e = { "[" + $global:row.tostring("D") + "]"; ++$global:row }; }

Try the new Code below:

## - 
## - To preserve the new 'row' column as part of the PSobject. by saving it as a new object.
## -
$myWindowsFilesList = dir c:\Windows -Recurse -ErrorVariable flerr -ErrorAction SilentlyContinue;
$row = 0; $myNewWindowsfilesList = $null;
$myNewWindowsfilesList = $myWindowsfilesList `
| Select-Object @{ l = 'row'; e = { "[" + $global:row.tostring("D") + "]"; ++$global:row }; }, `
name, mode, lastwritetime, length;

## - verify the 'Row' column has been added:
$myNewWindowsfilesList | Get-Member -MemberType Properties;

## - List a small range of rows:
$myNewWindowsfilesList[1633.. 1645] | Format-Table;

Add custom property using Add-Member

Add columns “SystemName” and “UserName“, to an existing new object using the “Add-Member” cmdlet within the ‘foreach{..}’ statement.

## - 
## - Using "Foreach" command to Add two new columns: "SystemName" and "Username"
## - to an existing PSObject.
## - 
[int32]$i = 0
foreach ($r in $myNewWindowsfilesList)
{ 
   ## - The following Add-member adds a property to an existing PSObject:
   $r | Add-Member -NotePropertyName "Systemname" -NotePropertyValue $env:COMPUTERNAME;
   $r | Add-Member -NotePropertyName "username" -NotePropertyValue $env:UserNAME;

   ## - Below code will show the progress bar:
   $percomplete = ($i / $myNewWindowsfilesList.count) * 100
   Write-Progress -Activity "Counting $i times" -PercentComplete $percomplete;
   ++$i
};

Displaying member properties and sample data with added columns:

## - verify the 'Row', 'computername', and 'username' columns has been added:
$myNewWindowsfilesList | Get-Member -MemberType Properties;

## - List a small range of rows with the new columns:
$myNewWindowsfilesList[1633.. 1645] | Format-Table;

Here’s where the PowerShell magic happened, as you would think that the Add-Member command would only affect only the row “$r” variable. The main PSObject “$myNewWindowsfilesList” has been updated. There’s no need to save the PSObject with a different name.

Recap

The combination of using ‘Custom Expressions’ with the Add-Member cmdlet gives great flexibility for building customized PSObjects. Go ahead and copy/paste this code, make your own properties, extend your PSObjects, and start thinking outside of the box.  Make it fun!

Bonus

In the script code you’ll find a few techniques you’ll want to pay attention to:

1. String “.NET formatting” for the ‘row’ column.

2. Use of the “-ErrorVariable” with the “-ErrorAction” parameters – prevents the errors or exceptions to be displayed in the console. For more information, use the command:
  Get-Help about_CommonParameters -ShowWindow;

3. Using the “Write-Progress” Cmdlet to display a progress bar while going thru adding the new columns. For more information, use the command:
Get-Help Write-Progress -ShowWindow;

4. “Get-Member” Cmdlet information, use the command:
Get-Help Get-Member -ShowWindow;

5. Displaying the saved errors from the $flerr variable;

## - 
## - BONUS - Check how many errors were saved, list a few of the messages
## -
$flerr.count
$flerr[50 .. 54].exception.message

PowerShell is a powerful scripting language. Enjoy and Keep Learning!
Own your future!

Installing PowerShell 7 in Ubuntu 20.04

Everyone knows, that sometime soon, Microsoft will support the PowerShell installation in Ubuntu 20.04. But, in the meantime, there’s another way. And, this also applies to WSL (Windows Subsystem for Linux) Ubuntu 20.04.

It is the way!

First, you must follow the instructions for installing .NET Core for Ubuntu 20.04 from the Microsoft Documentation: https://docs.microsoft.com/en-us/dotnet/core/install/linux-ubuntu#2004-

Basically, the following commands will install both the .NET Core SDK and the Runtime components:

## Install the SDK

wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb

sudo apt-get update; \
sudo apt-get install -y apt-transport-https && \
sudo apt-get update && \
sudo apt-get install -y dotnet-sdk-3.1

## Install Runtime

sudo apt-get update; \
sudo apt-get install -y apt-transport-https && \
sudo apt-get update && \
sudo apt-get install -y aspnetcore-runtime-3.1

sudo apt-get install -y dotnet-runtime-3.1

So, after the .NET Core gets installed, then proceed to install the “.NET Global” tool:

## - Install .NET Interactive:
dotnet tool install --global Microsoft.dotnet-interactive

## Install PowerShell Global:
dotnet tool install --global PowerShell

Almost there! There’s one more step we need to do. If you try executing PowerShell, the system can’t find the program.

To resolve the issue of PowerShell not found, we need to add the path to the .NET Global Tools components so that PowerShell can start.

In my case, I open my VIM editor using “sudo” so I can modify the “~/.bashrc” file.

## Add .NET Tools path in Bashrc
$ sudo vim ~/.bashrc
## - Add path to .NET Tools:
export PATH=$PATH:~/.dotnet/tools
:wq

## - Refresh session after updating bashrc:
$ source ~/.bashrc

At this point, now you can start using PowerShell 7 in Ubuntu 20.04.

But, how to update PowerShell?

Simple! The following two commands will update .NET Tools when the update becomes available:

dotnet tool update -g --add-source "https://dotnet.myget.org/F/dotnet-try/api/v3/index.json" Microsoft.dotnet-interactive
dotnet tool update -g powershell

And, the following command will confirm the latest version of both the .NET Tools installed in the system:

dotnet tool list --global

Now, go ahead and have fun with PowerShell.

Happy PowerShelling!!

Updating your .NET Tools components

Have you installed any of the .NET Tools? Such as “.NET Interactive” and “PowerShell Global“, then you’ll need to remember, to update these tools manually.

These tools give you the ability to use create Jupyter Notebook using Python Kernel but also with C#, F#, and PowerShell 7 kernels.

Check Current Version

First, need to list which .NET Tools are currently installed by using the following command:

dotnet tool list --global

In this sample, I opened a PowerShell 7 console and executed the command.

Manual Update

To update the tools, use the “dotnet tool …” command as follows:

1. To update the “Microsoft .NET Interactive” tool to the latest version:

dotnet tool update -g --add-source "https://dotnet.myget.org/F/dotnet-try/api/v3/index.json" Microsoft.dotnet-interactive

Completion message: (As of 07/30/2020, 16:20 PM)

Tool ‘microsoft.dotnet-interactive’ was successfully updated from version ‘1.0.136102’ to version ‘1.0.137901’.

2. To update PowerShell Global tool to the latest version:

dotnet tool update --global PowerShell

Completion message: (As of 07/30/2020, 16:20 PM)
Tool ‘powershell’ was successfully updated from version ‘7.0.2’ to version ‘7.0.3’.

*Note: If you have installed Anaconda, a manual update will be needed.

Keep in mind, these tools are not managed by Windows Update. So, you need to periodically run the update yourself.

This also applies to WSL 2 (Windows Subsystem for Linux).

More Information

WSL 2 – PowerShell Update-Help cmdlet is not working

Just recently I discovered, when executing the Update-Help cmdlet in WSL 2, that it doesn’t do anything.

Behavior: Run with no progress bar and no error messages at the end of the process. 

I have reported in the PowerShell Github repository and it will be addressed to the proper product group. This is on Windows 10 Version 2004, including Windows 10 Insider edition.

There are two workarounds to this issue:

Workaround #1

The workaround is shown below, thanks to Aditya Patwardhan (Microsoft PowerShell Developer) who provide the hint.

There are two Linux Bash environment variables that need to be updated: LANG and LC_ALL.

Check the current values using the echo command and, in my case, it shows:

## Current values:
(base) maxt@sapien01:~$ echo $LANG
C.UTF-8
(base) maxt@sapien01:~$ echo $LC_ALL
-EMPTY-
(base) maxt@sapien01:~$

Use the following “export” commands to change their values to be “en_US.UTF-8“:

(base) maxt@sapien01:~$
(base) maxt@sapien01:~$ export LC_ALL='en_US.UTF-8'
(base) maxt@sapien01:~$ export LANG='en_US.UTF-8'
(base) maxt@sapien01:~$
(base) maxt@sapien01:~$ echo $LC_ALL
en_US.UTF-8
(base) maxt@sapien01:~$ echo $LANG
en_US.UTF-8
(base) maxt@sapien01:~$

This will fix the issue temporarily during your WSL session, and the Update-Help will work properly.

For now, it may be needed to add these “export …” lines to the “~/.bashrc” file until the fix is available.

Workaround #2

Simply use the “Update-Help” specifying the UIculture:

Update-Help -uiculture en-us

That’s it!!

Keep PowerShelling!

Creating the PowerShell User Profile in Linux

In WSL, as on any Linux distribution, there’s no PowerShell User Profile file(“Microsoft.PowerShell_Profile.ps1“). So, it needs to be created manually.

Creating the profile folder

This profile is stored in the user home configuration folder “~/.config/powershell” folder.

But, the “powershell” folder doesn’t exist, it needs to be created in the configuration folder:

From the bash prompt, follow these steps:

1. Make sure you are in the user home folder:

pwd
cd /home/yourUserFolder

2. Verify the PowerShell folder doesn’t exist:

ls ~/.config

3. Change to the configuration folder:

cd ~/.config

3. Create the “powershell” folder, and assign permissions:

cd ~/.config
mkdir powershell
chmod 755
ll

Creating Microsoft.PowerShell_profile file

1. Using your Linux editor, create the Microsoft.PowerShell_Profile.ps1 file, and add code to the file: (Below using “vim” editor)

sudo vim /home/yourUserFolder/.config/powershell/Microsoft.PowerShell_profile.ps1
-> Write-Host "Welcome to PowerShell in Linux" -foreground 'Yellow';
-> Import-Module Microsoft.PowerShell.UnixCompleters
-> Import-UnixCompleters
-> Write-Host "UnixCompleters is loaded!" -foreground 'Yellow';

5. When done, save changes and exit “vim’ editor by typing:

:wq

Testing the PowerShell Profile

Open PowerShell and the “Welcome to PowerShell in Linux” with any other text will be displayed. At the same time, anything else in the profile will be executed.

Now, you can add more commands to the file when needed.

Keep on PowerShelling!