Category Archives: PowerShell

PowerShell 7 – Ternary Operator

In addition to pipeline chain operators which i discussed in my previous post. PowerShell 7 introduces another long awaited addition, a ternary operator. This operator can be used in place of conditional statements If and Else. The ternary operator follows the format of the C# language and takes three arguments.

<condition> ? <if-true> : <if-false>

Using a ternary operator can be seen as a shorthand way of writing an if-else statement in PowerShell.

if <condition> {
  do something
}
else
{
  do something else
}

Ternary operators are an interesting addition to PowerShell 7. It’s basically a simplified if-else statement that you can perform on one line. The first argument is the condition to evaluate. The second argument is a True evaluation response, and the third is a False evaluation response.

$total = 10 * 5 -eq 20 ? 'yes' : 'no'

$os = $IsWindows ? 'This is a Windows OS' : 'Not a Windows OS'

$ConfirmPreference -ne High ? ($ConfirmPreference = High) : 'Already High'

(Test-Path $PROFILE) ? (Write-output 'Profile exists') : (New-Item -ItemType file -Path $PROFILE -Force)

The condition argument will get evaluated to a Boolean True / False which will determine which part of the branch is evaluated and run. If you want to run commands you’ll need to wrap them in parenthesis.

Nesting is possible using a ternary operator. Nesting ternary operators should be kept simple. At first glance it can be seen a little cryptic. But once you understand how it works it starts to make sense.

 $IsMacOS ? 'You are on a Mac' : $IsLinux ? 'You are on Linux' : 'You must be on Windows'

If we write the above out in the traditional elseif way it starts to make more sense.

if ($IsMacOS) {
    'You are on a Mac'
} elseif ($IsLinux) {
    'You are on Linux'
} else {
    'You must be on Windows'
}

If you have used the Where-Object alias in the past you will know that it’s referenced as a ‘?‘. Under very rare situations should you find yourself having a conflict or undesirable behavior between the ternery ‘?’ and Where-Object alias ‘?’. What will be more important is being able to correctly identify code referencing a ternary ‘?’ vs a Where-Object alias.

Ternary operators in PowerShell aren’t without a little controversy. It’s been a feature that has been requested for many years. Jeffery Snover spoke about wanting them way back in PowerShell v1. PowerShell, though, has had the ability to perform something very similar on one line for some time.

# Pre ternary way
$var = if ($x) { $y } else { $z } 

# Ternary way
$var = $x ? $y : $z

It’s arguable which format is easier to understand. The traditional PowerShell way or the new ternary way??? While PowerShell users may find the former easier, programmers coming from languages like C# will no doubt be comfortable with using ternary operators in PowerShell. In any case I’d expect this to be a very niche operator rarely used, but time will tell I guess.

References
Ternary Operator RFC

PowerShell 7 – Pipeline Chain Operators

The GA release of PowerShell 7 is just around the corner.  With this release comes several new features that continue to build upon the previous versions.  One of these new features being introduced are two new operators, && and ||, referred to as pipeline chain operators.   They are intended to work like AND-OR lists in POSIX like shells (and to my surprise to learn, like conditional processing symbols in the Command Shell in Windows).

# Clone a Git repo and if successful display its README.md file
C:> $repo = "https://github.com/originaluko/haveibeenpwned.git"
C:> git clone $repo && Get-Content haveibeenpwned/README.md

Use of pipeline chain operators in PowerShell is fairly straight-forward.  The left-hand side of the pipeline will always run when using either of the operators.  The && operator will only execute the right-hand side of the operator if the left side of the pipeline was successfully executed.  Conversely the || operator it will only execute the right side of the pipeline if the left side fails to execute.

C:> Write-Output 'Left' && Write-Output 'Right'
Left
Right

C:> Write-Output 'Left' || Write-Output 'Right'
Left

Previously to achieve a similar outcome you might create a script block using an If statement.

C:> Write-Output 'Left'; if ($?) {Write-Output 'Right'}
Left
Right

You can also place multiple operators in the one pipeline.  These operators will be processed left-associative, meaning they are processed from left to right. 

C:> Get-ChildItem C:\temp\test.txt || Write-Error 'File does not exist' && Get-Content c:\temp\test.txt

In the above example || is processed before &&.  So Get-ChildItem and Write-Error can be seen as grouped first and processed before Get-Content.

[Get-ChildItem C:\temp\test.txt || Write-Error “File does not exist”] && Get-Content c:\temp\test.txt

To achieve something similar without pipeline chain operators, and this is where things get a little more interesting with the additional work involved, you might perform the below. 

C:> Get-ChildItem C:\temp\test.txt ; if (-not $?) {Write-Error "File does not exist"} ; if ($?) {Get-Content c:\temp\test.txt}

Care should be taken with commands that return a True / False boolean response. They should not be confused as successful and unsuccessful executions. For example something like Test-Path.

C:> Test-Path C:\temp\test.txt && Write-Output 'File exists'
True
Path exists

C:> Test-Path C:\temp\test.txt || Write-Output 'File exists'
False
Path exists

In both cases Test-Path successfully ran and in turn the right-hand side of the pipeline executes.  Pipeline chain operators work by checking the value of the $? variable.  This is an automatic variable which is set to either True or False based on the success of the last command. 

Pipeline chain operators can be used with Try / Catch blocks. It should be noted that script-terminating errors take precedence over chaining.

try
{
    $(throw) || Write-Output 'Failed'
}
catch
{
    Write-Output "Caught: "
}

In the above, while not elegant, a script-terminating error is caught and Caught: ScriptHalted is printed.

try
{
    Get-ChildItem C:\nonExistignFile.txt || Write-Output 'No File'
}
catch
{
    Write-Output "Caught: $_"
}

In the following example a non-terminating error happens when no file is found and ‘No File’ is printed.

One of the goals of pipeline chain operators is to be able to control your pipeline and actions based on command outcome rather than command output.  This is a slightly different approach than we might be use to in PowerShell.  Rather than having to validate output and then take an action we can immediately take that action based on the outcome of the previous command.

It’s going to be interesting to see how widely accept these new operators become. Many of us have made do without them since the beginning of PowerShell. Though we now have access to something that has been available in many of shells like bash and cmd.exe for a long time.

The pipeline chain operator RFC has a lot of good information and further explanation on its use.

References
Pipeline Chain Operators

Have I Been Pwned PowerShell Module v3

Over the last few years I’ve written I few posts on a PowerShell module I created that allows users to directly talk to the Have I Been Pwned API service (https://haveibeenpwned.com) that Troy Hunt maintains. While those posts are a little old now, they are still a good read on what this PowerShell Module is about. I encourage you to read them if you are interested (links at the bottom).

A few months back Troy made a big change to the way his API service works by requiring authorisation in the form of an API key. This broke a lot of different scripts and services the community have created that leveraged his service, including my own PowerShell module. Troy has discussed at length why he has decided to take these steps. I won’t bother going into it here. Authentication and the Have I Been Pwned API

Shortly after this change took effect I received a number of comments from the community that my PowerShell module didn’t work anymore. One or two even said that it was failing because I wasn’t providing an API key with the module. So I wanted to spend a few minutes to explain some of the new changes in the way the latest version of the Have I Been Pwned PowerShell module works. And what you need to do if you want to use it.

Firstly I decided to version increment the PowerShell module from the previous latest version of v1.4.2 to v3 to match the API version used by HIBP. (Version 2 was a short lived version up on my GitHub page)

Now for the big breaking change. Where applicable, all the URIs in the module have been updated to the v3 API. And again, where applicable, have had a header added to them to include a hibp-api-key value/token. Not all URI endpoints require an API Key. Generally speaking if you want to check for a pwned email address you will need an API key.

So how does this work?
The two functions that require an API key to be specified are Get-PwnedAccount and Get-PwnedPasteAccount. In the past you would have typed something like --

Get-PwnedAccount -EmailAdddress [email protected]

This would have returned all breached instances of sites that this email address would have been compromised in. In version 3 you now require the use of an API key to do the same thing.

Get-PwnedAccount -EmailAdddress [email protected] -apiKey "hibp-api-key"

So in this above example you can input your API key directly in the command. Or you could store it in a variable and call it at a later stage in the command. For example

$myApiKey = "xxxxxxxxxxxxx"
Get-PwnedAccount -EmailAdddress [email protected] -apiKey $myApiKey 

If you also really wanted to, you could hard code your API key in the parameters section of these scripts. Certainly not recommended but the choice is yours.

So where do I get this API key?
To make it clear, not from this PowerShell module or from me. You will need to go to Troy Hunt’s site (https://haveibeenpwned.com/API/Key) and purchase one.

Once you do you, this will be yours, or your organisation’s, own personal key that you do not share out. How you protect it and how you want to use it will be up to you.

Where can I download the PowerShell Module?
PowerShellGallery: https://www.powershellgallery.com/packages/HaveIBeenPwned/
GitHub: https://github.com/originaluko/haveibeenpwned

Previous Posts
HaveIBeenPwned PowerShell Module
HaveIBeenPwned PowerShell Module Updates

Display Last Command in PowerShell Title Bar

Carrying on from my previous post on pimping out your PowerShell console. I started to look into way to update the PowerShell title bar. In PowerShell there’s an automatic variable called $host where you can specify your own custom UI settings. One of the properties that can be modified is the console title bar in PowerShell. Now you say, “But Mark, why?”. Well I say, “But why not.”. And now that we have that formality out the way…

Changing the PowerShell console title is actually very simple and can be done on one line.

$Host.UI.RawUI.WindowTitle = 'Dream bigger.'

This command can be run from the command line or placed into your startup profile.ps1 file.

I initially placed quotes into the WindowsTitle property, placed the line at the bottom of my profile.ps1 file, and would get my startup profile to automatically load it when I ran a new PowerShell session. However, with my recent experimentation with the PowerShell prompt and the Get-History cmdlet. I had the idea of dynamically populating my the console title bar with my previous commands.

A lot of the leg work to do this is explained in my previous post, Display Execution Time In PowerShell Prompt. As such, i’m not going to delve into it too in depth here. Instead I do recommend looking at that post.

To update the console title with our previous command we leverage the cmdlet Get-History (just as I used in my previous post).

$host.ui.RawUI.WindowTitle = (Get-History)[-1]

This will update our console title with out last command, but it won’t continue to update after each subsequent command.

So we can take this one step further by updating the built-in PowerShell function Prompt. This function will run after each command is executed. We can modify the function by copying and pasting the below code into our PowerShell session and execute Prompt. This would work for our current PS session.

function Prompt {
  $history = Get-History
  $host.ui.RawUI.WindowTitle = ($history)[-1]
  return " "
}

Now better yet, we can update our startup profile file. Usually this is profile.ps1 held in C:\Users\{Username}\Documents\WindowsPowerShell\ for Windows PowerShell or C:\Users\{Username}\Documents\PowerShell\ for PowerShell Core. By pasting this code into our startup profile, it will execute each time we open a new PowerShell session automatically.

So there you have it. Another pointless awesome way to pimp our your PowerShell console. Combine this with Execution Time in your prompt and you have the flyest console around.

Display Execution Time In PowerShell Prompt

Some time back I attended a presentation where the presenter’s PowerShell console displayed their last command’s execution time in the prompt. At the time of thought it was a bit of a geeky novelty thing. Though recently I’ve had a slight change of opinion. It’s become a great way to easily see the efficiency of my code.

To make a pretty crude quote. There are many ways to skin a cat. PowerShell is extremely flexible in that it allows you to perform the same task many different ways. But not all ways are equal right?!? In the two examples below a perform a fairly basic count from 1 to 10 million.

$x = 0
ForEach ( $i in 1..10000000 ) {
        $x = $x + 1
    }
$x

In the above example the code runs in “around” 9834 milliseconds (9.8 seconds).

class MyClass
{
static[int] CountUp() 
    {
    $x = 0
    ForEach ( $i in 1..10000000 ) {
          $x = $x + 1
        }
    return $x
    }
}

[MyClass]::CountUp()

In this second example the code runs in 552 milliseconds (~0.5 seconds). A huge difference.

Being able to quickly and easily see execution time in the prompt can be quite helpful in determining if you’re coding as efficiently as you could be. It’s led me to trying things multiple ways before I settle. Now the actual code to display execution time is also quite easy to add into your PowerShell startup profile or to just run in your current session.

PowerShell comes with a built in prompt function which you can override with your own. In the below example I have created a new prompt function which I can execute by typing Prompt after running the code in my current session.

function Prompt {
  $executionTime = ((Get-History)[-1].EndExecutionTime - (Get-History)[-1].StartExecutionTime).Totalmilliseconds
  $time = [math]::Round($executionTime,2)
  $promptString = ("$time ms | " + $(Get-Location) + ">")
  Write-Host $promptString -NoNewline -ForegroundColor cyan
  return " "
  } 

The execution time of commands is retrieved from the StartExecutionTime and EndExecutionTime properties of Get-History. I get the time of the previous command, round to two decimal places, and write that to the prompt.

You can also take the function and place it in your PowerShell default startup profile file which will execute each time you open a new PowerShell session. It does require a slight modification to the above function which I’ll discuss later below. I’ve written a few posts on how to find and modify your default profile. But if your using Windows PowerShell you can find or add it in C:\Users\{Username}\Documents\WindowsPowerShell\profile.ps1. If your using PowerShell Core you can find or add it in C:\Users\{Username}\Documents\PowerShell\profile.ps1.

function Prompt {
  if ((Get-History).count -ge 1) {
  $executionTime = ((Get-History)[-1].EndExecutionTime - (Get-History)[-1].StartExecutionTime).Totalmilliseconds
  $time = [math]::Round($executionTime,2)
  $promptString = ("$time ms | " + $(Get-Location) + ">")
  Write-Host $promptString -NoNewline -ForegroundColor cyan
  return " "
  } else {
    $promptString = ("0 ms | " + $(Get-Location) + ">")
    Write-Host $promptString -NoNewline -ForegroundColor cyan
    return " "
  }
}

In the code above I’ve rapped it in an If / Else statement block. The logic here is that we use Get-History to get the execution time, but when a PowerShell session is first run there is nothing in Get-History, which will cause the function to fail and not run correctly generating a default vanilla prompt. Not ideal. So we create an else block and generate our own default prompt when no history of previous commands exist when initially opening a new PowerShell session.

So while many of you may also just find this a geeky novelty thing. It can also be a good reminder for you to try and keep your code and scripting as efficient as possible. Hey and at the very least you can impress your friends with a cool PowerShell prompt.

HaveIBeenPwned PowerShell Module Updates

Back in 2017 I wrote a post on a PowerShell module I created that consumes Troy Hunt’s Have I Been Pwned API service. I won’t go into too much detail about the service here. Plenty of people already have and since that time HaveIBeenPwned has exploded in popularity and most of us know what it is.

In that post I briefly discussed what the module does how you can begin to use some of the core functions in it. Since that time Troy has made a few changes to the API service, some small and some large, which I’ve slowly integrated into the PowerShell module. Things like UserAgent strings being a requirement and K-anonymity for password checks.

The community has also played a part in shaping the PowerShell module over the last year. I’ve had a lot of feedback and even some contributions through the GitHub project. It’s been pretty cool to receive PRs via my GitHub page for improvements to the module.

I thought now was a good opportunity for a follow-up post to talk about some of the changes and updates that have been made over the last year.

Probably the biggest change has been K-anonymity in Get-PwnedPassword. Originally you would send your password over the air in the body of a HTTPS request. With K-anonymity, Get-PwnedPassword will now SHA1 hash your password locally first and will always just send the first 5 characters of the hash to the HaveIBeenPwned API. It’s a much safer way of checking passwords which hopefully will lead to more people accepting and trying this method.

PS F:\Code> Get-PwnedPassword -Password monkey
AB87D24BDC7452E55738DEB5F868E1F16DEA5ACE
WARNING: Password pwned 980209 times!

I’ve attempted to make the module and all functions as PowerShell Core compliant as I can. I say, attempted, because as much of a fan of PowerShell Core as I am I keep finding differences in the way Core works. I’ve had to rewrite all the error handling to better catch 404 responses. A 404 not found response actually being a good thing in identifying that an email account has not be found in a breach. So whether it’s Windows PowerShell or PowerShell Core you should now be fine.

In my original post I gave an example of how you could run Get-PwnedAccount against a CSV file of email accounts and bulk check all your email addresses. Something that could be helpful in a corporate environment with many 100s of email addresses. The example I gave though was far from ideal.

This ability is now baked into Get-PwnedAccount and should lead for some interesting results. It’s very easy to use. A simple text file saved in CSV format with each email address on a separate line / row. Incorrectly formatted email addresses will be ignored and results are displayed only for identified email addresses in breaches.

Below is an example of what the CSV file might look like

[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]

Usage is straight forward too.

PS F:\Code> Get-PwnedAccount -CSV F:\emails.csv

Description                   Email             Breach
-----------                   -----             ------
Email address found in breach [email protected]    000webhost
Email address found in breach [email protected]    17
Email address found in breach [email protected]    500px

Each time an email is found in a breach it will output a result as an object. So you may get multiple results for a single email due to different breaches it’s in.

Identifying the total emails found in breaches is simple. For example

PS F:\Code> Get-PwnedAccount -CSV F:\emails.csv |  Measure-Object | Format-Table Count

Count
-----
  413

Now you probably don’t want to be hitting the API every time you want to manipulate the data. It will be slow and I can’t guarantee that rate limiting may block you. Storing the results in a variable will provide a lot more flexibility and speed. For example, finding results just on one email address

PS F:\SkyDrive\Code> $results = Get-PwnedAccount -CSV F:\emails.csv
PS F:\SkyDrive\Code> $results | Where-Object {$_.email -eq "[email protected]"}

Or if you don’t care about the breach and just want to display a compromised email address once.

$results | Sort-Object Email -Unique | Select-Object Email

You get the point right!?!? It’s fairly flexible once you store the results in an array.

Finally one last small addition. Get-PwnedAccount will now accept an email from the pipeline. So if you have another cmdlet or script that can pull an email out, you can pipe that directly into Get-PwnedAccount to quickly check if it’s been compromised in a breach. For example checking an AD user email address could be done as follows…

PS F:\code> Get-ADUser myuser -Properties emailaddress | % emailaddress | Get-PwnedAccount

Status Description              Account Exists
------ -----------              --------------
Good   Email address not found. False

The HaveIBeenPwned PowerShell module can be downloaded from the PowerShellGallery. Always make sure you are downloading and using the latest version. Within PowerShell use Install-Module -Name HaveIBeenPwned. The project can also be found on my GitHub page where you can clone and fork the project.

I’m keen to hear suggestions and feedback. So please let me know your experiences.

Download Links
PowerShellGallery: https://www.powershellgallery.com/packages/HaveIBeenPwned/
GitHub: https://github.com/originaluko/haveibeenpwned

5 Getting Started PowerShell Core Tips

Now that PowerShell Core 6 has gone GA, it’s a great time to start learning this new version.  So I thought I would put together 5 quick tips and tricks that I’ve used to make using PowerShell Core a little easier for myself while making the transition from Windows PowerShell.

While the intention of this post is not to go into the differences between Windows PowerShell and PowerShell Core.  Despite the slightly mixed or vague messaging we getting from Microsoft on the future of PowerShell.  I think it’s safe to say that PowerShell Core is not a replacement or upgrade for Windows PowerShell but it is the future of PowerShell.  As soon as more and more Modules start supporting PowerShell Core the argument to switch over will become easier.

So while people continue to argue if they should make the switch to PowerShell Core or not here are 5 tips and tricks I have used with PowerShell Core in the meantime.  It’s also worth noting that all of these tips are Windows specific.

Tip 1. Modify your default profile.

I wrote a post on this a little while back on how to modify your default profile in Windows PowerShell here.  I recommend taking a look at it and bring a bit of yourself to PowerShell.  Much of what I discuss in the post is still valid for PowerShell Core.  The only different really is your profile path in PowerShell Core.

You can easily locate your profile in PowerShell Core by typing $Profile at the command profile.  Unless you’ve already modified it, there’s a good chance that the location path doesn’t actually exist on your PC.

In Windows 10 the path is C:\Users\{username}\Documents\PowerShell\Microsoft.PowerShell_profile.ps1.  If it doesn’t exist go ahead and create the folder PowerShell in your Documents folder.  Then create a file called profile.ps1 and modify to your hearts content.  All the steps are laid out in my previous post I mentioned on modifying your Windows PowerShell profile. Without any specific Core modifications I took my already modified profile.ps1 profile from Windows PowerShell and copied it to this location.

Original PowerShell Core console on the left with my modified profile.ps1 profile on the right.

Tip 2. Modify VS Code to use PowerShell Core Integrated Terminal

While I still do like to use the Windows PowerShell ISE with ISE Steroids from time to time.  I’ve for the most part switched to VS Code for most of my day to day use.  If you haven’t yet made the switch I highly recommend downloading it and giving it try.  It’s fast, stable, and uses relatively little memory compared to the PowerShell ISE.

Currently as of VS Code 1.22 you can’t run multiple different Integrated Terminals.  But there’s still a few things we can do here.  We can change our default Integrated Terminal to PowerShell Core.

Open VS Code and navigate to File / Settings.  On the left you have your Default Settings and on the Right you have your User Settings to overwrite the Default Settings.

Enter in the following between the two { }

// PowerShell Core
“terminal.integrated.shell.windows”: “C:\\Program Files\\PowerShell\\6.0.0\\pwsh.exe”,

Now when you open up an Integrated Terminal it should default to PowerShell Core (pwsh.exe)

You can download VS Code from https://code.visualstudio.com/

Tip 3. Modify Console Window properties

This one kind of carries on from Tip 1.  More visual customisations on how a terminal session looks and is very much personal preference.  Right click on the top left and pull up the terminal properties.  I drop the Font size down to a non standard size of 13 which fixes what I feel is an overly elongated terminal.  Increase the Layout windows size to 130 x 40 and hard code a different background colour.  Finally I increase the Command History buffer size.

Tip 4. Use Chocolatey to Install and Upgrade PowerShell Core.

Chocolatey is a Windows Package Manager similar to apt-get or yum in the Linux world.  What I love about Chocolatey is that it handles installing and configuring all dependencies you require when installing an application.

Installing Chocolatey is as simple as running a few commands on one line in PowerShell.  Installing / Upgrading Powershell Core is just has simple once Chocolatey is installed.

The below command will install Chocolatey from a Powershell prompt.

Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString(‘https://chocolatey.org/install.ps1’))

This next command will install PowerShell Core and any dependencies it needs.  Replace install with upgrade if you have a previous version of PowerShell Core installed.

choco install powershell-core -y

You can download Chocolatey from their website at https://chocolatey.org/  and instructions to install PowerShell Core can be found at https://chocolatey.org/packages/powershell-core

Tip 5. Find some modules

PowerShell Core by default uses a different module path to load modules. Meaning that all your Windows PowerShell modules won’t just import and run. This doesn’t mean that they won’t work. PowerShell Core has been designed to be has backwards compatible as possible. While modules like Active Directory won’t work correctly many still do.

By installing the WindowsPSModulePath module from the PowerShell Gallery, you can use Windows PowerShell modules by appending the Windows PowerShell PSModulePath to your PowerShell Core PSModulePath.

First, install the WindowsPSModulePath module from the PowerShell Gallery

Install-Module WindowsPSModulePath -Force

Then run the Add-WindowsPSModulePath cmdlet to add the Windows PowerShell PSModulePath to PowerShell Core:

Add-WindowsPSModulePath

This will now allow you to now start using all your current Windows PowerShell modules in Core (in your current session).  This is not a guarantee that they will work though.  So test thoroughly.

If you’re after specifically supported PowerShell Core modules you can search for the tag PSEdition_Core in the PowerShell Gallery.

 

I hope some of the above quick tips help you get started with PowerShell Core and make the transition a little easier.

HaveIBeenPwned PowerShell Module

If you haven’t heard of Have I Been Pwned, firstly what are you doing?  It’s a site created by fellow Aussie Troy Hunt.  Troy aggregates data breaches as they become public into a searchable database. One of the primary goals of Have I Been Pwned is to raise security awareness around data breaches to the public.

As a bit of a learning exercise to myself, I created a PowerShell Module that leverages the haveibeenpwned.com APIs.  The module contains five Functions, Get-PwnedAccount, Get-PwnedBreach, Get-PwnedDataClass, Get-PwnedPassword, and Get-PwnedPasteAccount. I like to think of the HaveIBeenPwned PowerShell Module as an Enabler. By itself it does nothing more than what the haveibeenpwned.com site does. But by leveraging the Power of PowerShell and returning the results in object format the data can be easily manipulated for many other purposes.

Installing and using the Module and Functions is very simple. Ideally you will be running PowerShell 5 or above which will allow you to easily download and install from the PowerShellGallery. If you’re not on PowerShell 5 I’d highly recommend you download the WMF 5.1 (Windows Management Framework) which includes PowerShell 5.

Installing the module is simply a matter of typing the following.

PS F:\Code> Install-Module -Name HaveIBeenPwned

Once installed you can view all the Functions available with the following command.

PS F:\Code> Get-Command -Module haveibeenpwned 

CommandType     Name                                               Version    Source                                                                               
-----------     ----                                               -------    ------                                                                               
Function        Get-PwnedAccount                                   1.1        HaveIBeenPwned                                                                       
Function        Get-PwnedBreach                                    1.1        HaveIBeenPwned                                                                       
Function        Get-PwnedDataClass                                 1.1        HaveIBeenPwned                                                                       
Function        Get-PwnedPassword                                  1.1        HaveIBeenPwned                                                                       
Function        Get-PwnedPasteAccount                              1.1        HaveIBeenPwned      

The two main Functions are Get-PwnedAccount and Get-PwnedPassword.

The first, Get-PwnedAccount, will enumerate if an account, based off an email address, has been found in the Have I Been Pwned list of data breaches.

PS F:\Code> Get-PwnedAccount -EmailAddress [email protected]

In the above example all breaches are listed where the account used [email protected] as the email address. Which is huge by the way.

The second and slightly more controversial, Get-PwnedPassword, will take a password and confirm if it has been identified in a data breach.  Get-PwnedPassword will accept a password in three different formats.  Plain text, Secure String, and SHA1 hash.

PS F:\Code> Get-PwnedPassword -SHA1 AB87D24BDC7452E55738DEB5F868E1F16DEA5ACE

In the above example a SHA1 hash was generated offline using Quick Hash GUI.  Get-PwnedPassword will then send that Password or SHA1 hash in the body of a HTTPS request to Have I Been Pwned.  Now, obviously, what can been see as the controversial part off this is not only do you have to trust Have I Been Pwned but also this PowerShell Function.

All Functions come with Help and Examples which can be view using Get-Help.  For example.

PS F:\Code> Get-Help Get-PwnedPassword -Examples

The Module and all Functions can be found in the PowerShellGallery for download.  The Module can also been found in my public GitHub Project https://github.com/originaluko/haveibeenpwned.  All code can been view and sanity checked and is free to consume.

 

Lastly, I thought I might show how you can go one step further from simply enumerating an individual account. Many organisation’s IT departments create and manage accounts for their staff. They also provide security awareness training in protecting online accounts. An organisation could take a CSV list of their staff’s email addresses, import that list into PowerShell, and run it against the Get-PwnedAccount Function and identify if any of their staff have been involved in a data breach.

In the below example I import a small CSV file I have created with a list of email addresses. Then using half a dozen lines of code I iterate through the CSV list of email addresses and identify all the accounts that have been involved in a data breach. Using this information I can pro-actively notify staff to review these accounts.

$emails = Import-Csv F:\email_list.csv
foreach ($email in $emails) {
    $email = $email.accounts
    $results = Get-PwnedAccount -EmailAddress $email
    if ($results.status -ne 'Good') {
        foreach ($result in $results) { 
            $breach = $result.title
            Write-Output "Email address $email has been found in a $breach breach"
        }
    }
    Start-Sleep -Milliseconds 1500
}

And sample output after running the above code.

Email address [email protected] has been found in a Yahoo breach
Email address [email protected] has been found in a Youku breach
Email address [email protected] has been found in a Zomato breach
Email address [email protected] has been found in a 000webhost breach
Email address [email protected] has been found in a 17 breach
Email address [email protected] has been found in a Adobe breach
Email address [email protected] has been found in a Bell (2017 breach) breach

 

Also read the follow up post on new additions
HaveIBeenPwned PowerShell Module Updates -- https://blog.ukotic.net/2019/05/28/haveibeenpwned-powershell-module-updates/

Download Links
PowerShellGallery: https://www.powershellgallery.com/packages/HaveIBeenPwned/
GitHub: https://github.com/originaluko/haveibeenpwned

Could not establish trust relationship for the SSL/TLS Secure Channel – Invoke-WebRequest

I’ve recently been playing around with VMware’s REST APIs in VCSA 6.5 using PowerShell. I’ve been using a lot of Invoke-WebRequest and Invoke-RestMethod to do my work. Chris Wahl has a great primer on how to get started here.

One issue that I ran into very quickly working again my VCSA was a certificate trust relationship error. I’ve run into this error numerous times in the past.

PS F:\Code> Invoke-WebRequest -Uri https://10.0.0.201/rest/com/vmware/cis/session -Method Post -Headers $head
Invoke-WebRequest : The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
At line:1 char:1
+ Invoke-WebRequest -Uri https://10.0.0.201/rest/com/vmware/cis/session ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

The first time I ran into this error I was stumped for while finding a solution. Ultimately it comes down to using Self-Signed Certificates in vCenter, as most of us do.  In general using Invoke-WebRequest or Invoke-RestMethod against a server using a Self-Signed Certificate will cause this error, it’s not just related to vCenter.

The solution is quite simple.  I found a snippet of code some time back that I keep on hand in this situation.  It basically ignores certificate validate in PowerShell allowing you to make a connection with Invoke-WebRequest.  All you have to do it paste this code into your PowerShell session before you run Invoke-WebRequest against a server with a Self-Signed Certificate.

if (-not ([System.Management.Automation.PSTypeName]'ServerCertificateValidationCallback').Type)
{
$certCallback = @"
    using System;
    using System.Net;
    using System.Net.Security;
    using System.Security.Cryptography.X509Certificates;
    public class ServerCertificateValidationCallback
    {
        public static void Ignore()
        {
            if(ServicePointManager.ServerCertificateValidationCallback ==null)
            {
                ServicePointManager.ServerCertificateValidationCallback += 
                    delegate
                    (
                        Object obj, 
                        X509Certificate certificate, 
                        X509Chain chain, 
                        SslPolicyErrors errors
                    )
                    {
                        return true;
                    };
            }
        }
    }
"@
    Add-Type $certCallback
 }
[ServerCertificateValidationCallback]::Ignore()

Once you run the code you will be able to now successfully make a connection.

I’ve seen some simple one liner solutions for Self-Signed Certificates but none of them seemed to work for me.  Whereas the above snippet of code has always worked.  Obviously bypassing certificate validate is not something you want to run on a global scale in PowerShell but this code works great for your current session only.

If there is a simpler way to bypass certificate validation I’d love to hear it.

Store Multiple Pure Storage Connections In A PowerShell Array

I’ve recently been playing around with the Pure Storage PowerShell modules. I’ve found the Pure cmdlets to be quite extensive and easy to use. Quite a nice change from PowerShell Cmdlets of other traditional storage vendors. One thing, though, that I found a little annoying was that I had to store a connection for a Pure Array into a PowerShell object and constantly reference that object in each cmdlet I ran. Not a big deal normally but where I ran into an issue was wanting to connect to multiple Pure Arrays at the same time and being able to run and iterate against them all at the same time. I quickly came to realise that the cmdlets themselves are designed to run against one Pure Array at a time.

Initially I thought I could store multiple connections to a variable using the += operator. But this lead to the following error.

C:\Code>   $arrays = New-PfaArray -EndPoint purearray1 -ApiToken 'b2342442-ebb2-5673-a452-c443f562cb7' -IgnoreCertificateError

C:\Code>   $arrays += New-PfaArray -EndPoint purearray2 -ApiToken '6523ff23-32ac-2890-9843-2e4e9543672' -IgnoreCertificateError
Method invocation failed because [PurePowerShell.PureArray] does not contain a method named 'op_Addition'.
At line:1 char:1
+ $array += New-PfaArray -EndPoint purearray2 -A ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (op_Addition:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound

A quick inspection of the data type of the variable created using GetType shows that it is a System.Object and not an Array. By default creating a connection to a Pure Array using New-PfaArray and storing that to a variable will cast it as an object.

C:\Code>   $arrays.GetType()

IsPublic IsSerial Name                                     BaseType          
-------- -------- ----                                     --------    
True     False    PureArray                                System.Object

This is easily fixed by setting the data type for our variable to [array] when we create it.

[array]$arrays = New-PfaArray -EndPoint purearray1 -ApiToken 'b2342442-ebb2-5673-a452-c443f562cb7' -IgnoreCertificateError
[array]$arrays += New-PfaArray -EndPoint purearray2 -ApiToken '6523ff23-32ac-2890-9843-2e4e9543672' -IgnoreCertificateError

Now when we check the data type we see it’s System.Array.

C:\Code>   $arrays.GetType()

IsPublic IsSerial Name                                     BaseType      
-------- -------- ----                                     --------       
True     True     Object[]                                 System.Array    

Checking the variable again we can see we have two records.

C:\Code>   $arrays

Disposed : False
EndPoint :
UserName :
ApiVersion : 1.7
Role : StorageAdmin
ApiToken : b2342442-ebb2-5673-a452-c443f562cb7

Disposed : False
EndPoint :
UserName :
ApiVersion : 1.7
Role : StorageAdmin
ApiToken : 6523ff23-32ac-2890-9843-2e4e9543672

Using this new variable with a Pure Storage Cmdlet is just a matter of specify the line in the array representing the Pure Storage Array we want using square brackets.

C:\Code>   Get-PfaArrayId -Array $arrays[0]

version revision             array_name           id                                  
------- --------             ----------           --  
4.8.10 201705102013+977fb3c  purearray1           b2342442-ebb2-5673-a452-c443f562cb7b

Where this array we created really becomes handy is when using it with foreach loops. We can now rap our Cmdlets in a foreach loop and iterate through all our Pure Storage Arrays.

C:\Code>   $results = foreach ($array in $arrays) {
Get-PfaArrayId -array $array
}

C:\Code>   $results | ft

version revision             array_name           id                                  
------- --------             ----------           --    
4.8.10 201705102013+977fb3c  purearray2           6523ff23-32ac-2890-9843-2e4e9543672
4.8.10 201705102013+977fb3c  purearray1           b2342442-ebb2-5673-a452-c443f562cb7

This is just a simple example but now we can start enumerating across all our Pure Storage arrays and easily start manipulating objects returned.

I really like the Pure Storage PowerShell modules but I really hope that a future update allows for easier working with multiple Pure Arrays. Hopefully allowing their Cmdlets to work against multiple arrays at the same time.