Adding tab-completion to your PowerShell Functions

 

upgrade-your-code

This post is part of the series on AutoCompletion options for PowerShell! Click the banner for more posts in the series!


Probably my single favorite feature of PowerShell isn’t exciting to most people…but I love Auto-Completion.  I have my reasons:

As I have the typing skills of a preying mantis (why did I mention them…they’re easily the creepiest and worst insect…ewww) and constantly typo everything, I LOVE auto-completion.

Add to that the fact that I have lost a memory competition to a gold fish, and I REALLY Depend upon it.

goldfish_1
If you have a memory like me, and like this guy, you’ll love Auto-complete

PowerShell helps deeply flawed people like me by offering tons of built-in help and autocomplete practically everywhere.  Some of it is done for us, automatically, while others require a bit more work from us as toolmakers in order to enable the sweet sweet tab expansion.

In the world of AutoCompletion, there are two real types of AutoComplete that PowerShell offers. In this series, we’ll cover these two types of PowerShell autocompletion:

  • Part 1  – (This post) Parameter AutoComplete
  • Part 2 – (Coming soon) Output AutoComplete

This post is going to be all about the first one.

Parameter AutoComplete

In PowerShell, when you define a Function, any of your parameter names are automatically compiled and available via autocompletion.  For instance, in this very simple function:

Function Do-Stuff {
param(
    $Name,$count)

    For($i = 1 ; $i -le $count; $i++){

        "Displaying $name, time $i of $count"

    }

}

As you’ll see in the GIF below, PowerShell will compile my function and then automatically allow me to tabcomplete through the available parameter names. Continue reading

Advertisements

Tool-highlight: Show Windows Toast Messages with PowerShell

Happy New Years, everyone!

This will be a quick post here, but I just wanted to shine a spotlight on an AWESOME tool that I absolutely love: Joshua King’s ‘BurntToast’ PowerShell module, which makes the arduous task of rendering a Windows Toast notification VERY Easy.

Check out his GitHub repo here, and view the module’s page on the PowerShell gallery here.

Here’s an example of what I’m talking about

en

Why might I want to use this?

Any time you want to provide data to the end-user, but not require them to drop everything to interact. I don’t know about you, but I really dislike alert dialog boxes.  Especially if they lock my whole desktop until I quickly ignore it and click the ‘X’ button…err, read it.

I also believe that toasts are what users expect, especially to receive updates from long-running scripts.  They really do provide a polished, refined look to your scripts.

Finally, you can also provide your own image and play your own sound effects too!

Real-time encryption notices

At a current customer, we’re deploying a device management profile using MDM to use BitLocker encryption on these devices.  We decided that it would be very useful to be able to see updates as a device was encrypting, so I wrote up this script around the BurntToast tool.

install-module BurntToast -Force
Import-module BurntToast

$EncryptionStatus = Get-BitLockerVolume -MountPoint c:

    While ($EncryptionStatus.VolumeStatus -eq 'EncryptionInProgress'){

        if (($EncryptionStatus.EncryptionPercentage % 5)-eq 0){
            New-BurntToastNotification -Text 'Encryption Progress', "Now $($EncryptionStatus.EncryptionPercentage)% completed."
        }

        Start-Sleep -Seconds 30

        $EncryptionStatus = Get-BitLockerVolume -MountPoint c:
        Write-host $EncryptionStatus.EncryptionPercentage
        }

New-BurntToastNotification -Text 'Encryption Completed' 'Now completed.' -Image "C:\Users\sred1\Dropbox\Docs\blog\foxderp - Copy.png"

And a screen shot of it in action!

encryption-percentage

Registering for WMI Events in PowerShell

registering-for-wmi-events

An alternate title might be ‘Running PowerShell Code ONLY when the power state changes’, because that was the very interesting task I received from my customer this week.

It was honestly too cool of a StackOverflow answer NOT to share, so here it goes, you can vote for it here if you thought it was worth-while.

If you want your code to trigger only when the System Power State changes, as described here, use this code.


Register-WMIEvent -query "Select * From Win32_PowerManagementEvent" `
 -sourceIdentifier "Power" `
 -action {
     #YourCodeHere
      }

Now, this will trigger whenever the power state changes, whether you plug the device in, OR unplug it. So you might further want to stop and pause to ask the question:

Am I on power or not?

Fortunately we can use the WMI Class Win32_BatteryStatus to detect if we’re charging or not, so here’s the full construct that I use to ONLY run an operation when a power event changes, and then only if I’m no longer on Power.

Locking the workstation when the system is unplugged


Register-WMIEvent -query "Select * From Win32_PowerManagementEvent" `
  -sourceIdentifier "Power" `
  -action {
      if ([BOOL](Get-WmiObject -Class BatteryStatus -Namespace root\wmi).PowerOnLine ){
         #Device is plugged in now, do this action
         write-host "Power on!"
     }
    else{
        #Device is NOT plugged in now, do this action
        write-host "Now on battery, locking..."
        [NativeMethods]::LockWorkStation()
     }

If you’re curious how this looks in real time

Registering for device events

It can also be useful to have your code wait for something to happen with devices, such as running an action when a device is added or removed. To do this, use this code.


#Register for power state change
#Where TargetInstance ISA 'Win32_Process'"
Register-WMIEvent -query "Select * From Win32_DeviceChangeEvent where EventType = '2'" `
-sourceIdentifier "Power" `
-action {#Do Something when a device is added
Write-host "Device added at $(Get-date)"
}

You might also want to do an action if a device is removed instead, so use this table to choose which event is right for you. Read more about it here.

EventType Id
ConfigurationChanged 1
Device Arrived 2
Device Removed 3
Device Docked 4

What else can I wait for?

Not only these, but you can trigger your code to execute on a variety of useful WMI Events, all of which can be seen in this image below!

ClassName Triggers when
Win32_DeviceChangeEvent  A device is installed, removed, or deleted, or the system is docked
Win32_VolumeChangeEvent Something happens to your disk drives
Win32_PowerManagementEvent Your device is plugged, unplugged or docked
Win32_ComputerSystemEvent Something major happens to the system
Win32_ComputerShutdownEvent The system is shutting down!
RegistryEvent Anythign happens to the registry
RegistryKeyChangeEvent A reg key you specify is changed
RegistryValueChangeEvent A reg value you specify is changed

Locking your Workstation with PowerShell

locking-your-workstation

Locking a workstation using PowerShell?  It sounds like an easy task, right?  That’s what I thought too…and told the customer…but NO!  Friends, it wasn’t easy…before now.

As it turns out, some tasks in Windows just aren’t accessible via WMI.  For instance, the useful Win32_OperatingSystem class has some nifty methods for working with the system’s power state, like Reboot and Shutdown…but strangely none for locking the system!

01

Then I stumbled upon this useful post by Ed over at The Scripting Guys, but this was back in the dark ages of VBScript, and unfortunately the only answer they found was to use Rundll32.exe to call a method in a dll and that, frankly will not fly.  You’ll hear the shrillest high and lowest lows over the radio, and my voice will guide you home, they will see us waving from such great heights–

Sorry, that phrase is still a trigger word for me and takes me back to my deeply embarrassing emo phase…moving right along.

How to work with native methods easily in PowerShell

If you want to know how this is done…stop right here and read this awesome blog post by Danny Tuppenny on the topic.  It’s eye-wateringly in-depth.  But if you just want an example of how it is done, lets proceed.

Now, we all know by now that we can use Add-Type to work with native C# code…but the brilliant thing that Danny did is create a function which just makes it very easy to import a dll and get at the methods within…then surface those methods as a new class.  It’s the bomb.com.

# Helper functions for building the class
$script:nativeMethods = @();
function Register-NativeMethod([string]$dll, [string]$methodSignature)
{
    $script:nativeMethods += [PSCustomObject]@{ Dll = $dll; Signature = $methodSignature; }
}
function Add-NativeMethods()
{
    $nativeMethodsCode = $script:nativeMethods | % { "
        [DllImport(`"$($_.Dll)`")]
        public static extern $($_.Signature);
    " }

    Add-Type @"
        using System;
        using System.Runtime.InteropServices;
        public static class NativeMethods {
            $nativeMethodsCode
        }
"@
}

With that done, we’ll now have some a function available to us, Register-NativeMethod. To use this, we simply provide the name of the .dll we want to use, and then what’s known as the method signature. For instance, let’s say I wanted to use User32.dll to move a window, as described here. Here’s the method signature for that method.

BOOL WINAPI MoveWindow(
  _In_ HWND hWnd,
  _In_ int  X,
  _In_ int  Y,
  _In_ int  nWidth,
  _In_ int  nHeight,
  _In_ BOOL bRepaint
);

The hWnd is kind of a special variable, it means HandlerWindow, or MainWindowHandle. You can get a MainWindowHandle by running Get-Process Name | select MainWindowHandle. All of the other values are just integeres, so that would be the window position in X and Y and the width and height. Finally, you can provide a true, false value with bRepaint (but I didn’t bother).

We can implement this in PowerShell by using the Register-NativeMethod function, like so:

Register-NativeMethod "user32.dll" "bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight)"

Finally, we call it like so:

#Find the first Notepad process' MainWindowHandle
$Handle = Get-Process notepad | select -first 1 -expand MainWindowHandle
[NativeMethods]::MoveWindow($Handle, 40, 80, 400, 400)

And here’s how it looks in practice.

gif

If you’d like to know what other Methods are available, you can turn to the lovely Pinvoke website which has a listing of every method available from all of these dlls.  And you can just plug and play them all, easily!

Particularly of note are methods in user32.dll and kernel32.dll, but deep-linking doesn’t work, so you’ll have to click the dll name on the left column.

But what about locking the WorkStation?

I didn’t forget about you!  To lock the workstation, run

Register-NativeMethod "user32.dll" "bool LockWorkStation()"

#Calling the method to lock it up
[NativeMethods]::LockWorkStation()

Complete Code

# Helper functions for building the class
$script:nativeMethods = @();
function Register-NativeMethod([string]$dll, [string]$methodSignature)
{
    $script:nativeMethods += [PSCustomObject]@{ Dll = $dll; Signature = $methodSignature; }
}
function Add-NativeMethods()
{
    $nativeMethodsCode = $script:nativeMethods | % { "
        [DllImport(`"$($_.Dll)`")]
        public static extern $($_.Signature);
    " }

    Add-Type @"
        using System;
        using System.Runtime.InteropServices;
        public static class NativeMethods {
            $nativeMethodsCode
        }
"@
}


# Add methods here

Register-NativeMethod "user32.dll" "bool LockWorkStation()"
Register-NativeMethod "user32.dll" "bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight)"
# This builds the class and registers them (you can only do this one-per-session, as the type cannot be unloaded?)
Add-NativeMethods

#Calling the method
[NativeMethods]::LockWorkStation()

Hands-off deployments

 

handsoff

Let’s face it, guys.  There are times that you JUST don’t have access to SCCM, MDT or Kace, and need to deploy a completely automated and silent Windows install without our normal build tools.  If this is you, and you deploy systems frequently, you’ve probably spent way too much time looking at screens like this one

wicd-1

Not only does it stink to have to type a user name and password every time, it also slows you down. Admit it, whenever you start a Windows install, you start doing something else, and then an hour later check back and have to reload the whole task in your memory again.  It’s a giant waste of time and makes you less productive.

To top it off, there are probably things you always do, like setup user accounts, join a machine to a domain, and set the time zones (we can’t all live in the chosen timezone of Pacific Standard Time).

Previously, making these changes and baking them in to an unattended install meant using the terrible Windows SIM tool, which was horrible.  Seriously, no offense meant, but if you had a hand in designing the System Image Manager tool, I’m sure you’re already ashamed.  Good, you should be.

Thankfully we now have the Windows Image Configuration Designer (Wicd) which makes this all super easy!

In this post, we’ll walk you through everything you need to do to make a fully silent, unattended Windows Install, along with some useful settings too.  We will be installing WICD, which is part of the ADK, and then walk through configuring the following settings:

  • ‘Enable Remote Desktop out of the box’

  • Set Default Time zone (no west coast time!)

  • Set Default First User

  • Silent Install (depends on setting a user account)

  • Make the computer do a quick virus scan on first boot

  • Optional – Domain Join

  • Optional – Add files to the image

  • Optional – Make Registry Changes on the Image

Setting up WICD

To get access to the awesome WICD tool, you’ll need to have the Windows 10 ADK.  I recommend using version 1607, at a minimum (Download Link).  When installing the ADK make sure to check the two red boxes shown below, for full features.

wicd-2
If you leave these unchecked, it won’t be WICD good.  Make sure to ☑️

Continue reading