Happy Star Wars Day

2 CommentsPowerShell

It’s the age-old* question people have been asking since at least 2006. Which is nerdier, Star Wars or PowerShell? While the debate rages on, we’ve taken on a different task. How can we join the two together for the ultimate mashup of fandom meets technical prowess?

While it may be an unconventional use of PDQ Deploy (running a non-silent pun intended interactive windowed deployment), it’s still a fun experiment.

Behind the Scenes

Here is an excerpt of the trumpet part. Copy it into your PowerShell ISE and give it a run.

$sw = [Diagnostics.Stopwatch]::StartNew()
$host.ui.RawUI.BackgroundColor = "Blue"
$host.ui.RawUI.ForegroundColor = "White"

function Pause-Console
 param([int]$p = 0,[int]$n = 0)
 $playhead = $p
 $next = $n
 $savedColor = $host.ui.RawUI.BackgroundColor

 $elapsed = $sw.elapsedMilliseconds
 $pelapsed = $elapsed - $playhead
 $dur = $next-$playhead

 if($pelapsed -lt 0) {
  $pos = [math]::abs($pelapsed) + $dur
  if($pos -gt 200) {
   $host.ui.RawUI.BackgroundColor = "Black"
  Write-Output "Start-Sleep -m $pos"
  Start-Sleep -m $pos
  if($pos -gt 200) {
   $host.ui.RawUI.BackgroundColor = $savedColor
  $dif = $dur - $pelapsed
  if($dif -gt 0) {
   if($dif -gt 200) {
    $host.ui.RawUI.BackgroundColor = "Black"
   Write-Output "Start-Sleep -m $dif"
   Start-Sleep -m $dif
   if($dif -gt 200) {
    $host.ui.RawUI.BackgroundColor = $savedColor

function Console-Beep
  param( [int]$h = 0,
    [int]$p = 0,
     [int]$n = 0)
  $hertz = $h
  $playhead = $p
  $next = $n
  $elapsed = $sw.elapsedMilliseconds
  $pelapsed = $elapsed - $playhead
  $dur = $next-$playhead

 if($pelapsed -lt 0) {
  $pos = [math]::abs($pelapsed)
  $dur = $dur+$pos
  Write-Output "[console]::beep($hertz,$dur)" 
  $dif = $dur - $pelapsed
  if($dif -gt 0) {
   Write-Output "[console]::beep($hertz,$dif)" 

Console-Beep -h 880 -p 566 -n 778
Pause-Console -p 778 -n 1132
Console-Beep -h 880 -p 1132 -n 1344
Pause-Console -p 1344 -n 1698
Console-Beep -h 880 -p 1698 -n 1910
Pause-Console -p 1910 -n 4867
Console-Beep -h 466 -p 4867 -n 4909
Pause-Console -p 4909 -n 4926
Console-Beep -h 466 -p 4926 -n 4968
Pause-Console -p 4968 -n 4989
Console-Beep -h 466 -p 4989 -n 5029
Pause-Console -p 5029 -n 5051
Console-Beep -h 466 -p 5051 -n 5107
Pause-Console -p 5107 -n 5119
Console-Beep -h 466 -p 5119 -n 5197
Pause-Console -p 5197 -n 5281
Console-Beep -h 349 -p 5281 -n 5338
Pause-Console -p 5338 -n 5449
Console-Beep -h 622 -p 5449 -n 5515
Pause-Console -p 5515 -n 5664
Console-Beep -h 698 -p 5664 -n 5742
Pause-Console -p 5742 -n 5864
Console-Beep -h 622 -p 5864 -n 5932
Pause-Console -p 5932 -n 6055
Console-Beep -h 466 -p 6055 -n 6140
Pause-Console -p 6140 -n 6270
Console-Beep -h 466 -p 6270 -n 6355
Pause-Console -p 6355 -n 6461
Console-Beep -h 349 -p 6461 -n 6540
Pause-Console -p 6540 -n 6661
Console-Beep -h 622 -p 6661 -n 6721
Pause-Console -p 6721 -n 6874
Console-Beep -h 698 -p 6874 -n 6946
Pause-Console -p 6946 -n 7074
Console-Beep -h 622 -p 7074 -n 7134
Pause-Console -p 7134 -n 7261
Console-Beep -h 466 -p 7261 -n 7330
Pause-Console -p 7330 -n 7470
Console-Beep -h 466 -p 7470 -n 7545
Pause-Console -p 7545 -n 7670
Console-Beep -h 349 -p 7670 -n 7751
Pause-Console -p 7751 -n 7874
Console-Beep -h 622 -p 7874 -n 7946
Pause-Console -p 7946 -n 8082
Console-Beep -h 698 -p 8082 -n 8155
Pause-Console -p 8155 -n 8311
Console-Beep -h 466 -p 8311 -n 8377
Pause-Console -p 8377 -n 8389
Console-Beep -h 466 -p 8389 -n 8482
Pause-Console -p 8482 -n 8545
Console-Beep -h 466 -p 8545 -n 8639
Pause-Console -p 8639 -n 8832
Console-Beep -h 349 -p 8832 -n 8995
Pause-Console -p 8995 -n 9151
Console-Beep -h 349 -p 9151 -n 9295
Pause-Console -p 9295 -n 9470
Console-Beep -h 349 -p 9470 -n 9626
Pause-Console -p 9626 -n 9882
Console-Beep -h 466 -p 9882 -n 11051
Pause-Console -p 11051 -n 11114
Console-Beep -h 698 -p 11114 -n 12116
Pause-Console -p 12116 -n 12213
Console-Beep -h 622 -p 12213 -n 12276
Pause-Console -p 12276 -n 12343
Console-Beep -h 587 -p 12343 -n 12424
Pause-Console -p 12424 -n 12491
Console-Beep -h 523 -p 12491 -n 12554
Pause-Console -p 12554 -n 12662
Console-Beep -h 932 -p 12662 -n 13709
Pause-Console -p 13709 -n 13769
Console-Beep -h 698 -p 13769 -n 14238
Pause-Console -p 14238 -n 14353
Console-Beep -h 622 -p 14353 -n 14444
Pause-Console -p 14444 -n 14528
Console-Beep -h 587 -p 14528 -n 14638
Pause-Console -p 14638 -n 14734
Console-Beep -h 523 -p 14734 -n 14821
Pause-Console -p 14821 -n 14959
Console-Beep -h 932 -p 14959 -n 16144
Pause-Console -p 16144 -n 16171
Console-Beep -h 698 -p 16171 -n 16646
Pause-Console -p 16646 -n 16788
Console-Beep -h 622 -p 16788 -n 16885
Pause-Console -p 16885 -n 16963
Console-Beep -h 587 -p 16963 -n 17063
Pause-Console -p 17063 -n 17163
Console-Beep -h 622 -p 17163 -n 17260
Pause-Console -p 17260 -n 17369
Console-Beep -h 523 -p 17369 -n 18723
Pause-Console -p 18723 -n 18856
Console-Beep -h 349 -p 18856 -n 18913
Pause-Console -p 18913 -n 18950
Console-Beep -h 349 -p 18950 -n 19025
Pause-Console -p 19025 -n 19050
Console-Beep -h 349 -p 19050 -n 19125
Pause-Console -p 19125 -n 19165
Console-Beep -h 466 -p 19165 -n 20359
Pause-Console -p 20359 -n 20378
Console-Beep -h 698 -p 20378 -n 21528
Pause-Console -p 21528 -n 21578
Console-Beep -h 622 -p 21578 -n 21663
Pause-Console -p 21663 -n 21763
Console-Beep -h 587 -p 21763 -n 21848
Pause-Console -p 21848 -n 21959
Console-Beep -h 523 -p 21959 -n 22065
Pause-Console -p 22065 -n 22181
Console-Beep -h 932 -p 22181 -n 23354
Pause-Console -p 23354 -n 23381
Console-Beep -h 698 -p 23381 -n 23960
Pause-Console -p 23960 -n 23975
Console-Beep -h 622 -p 23975 -n 24075
Pause-Console -p 24075 -n 24175
Console-Beep -h 587 -p 24175 -n 24248
Pause-Console -p 24248 -n 24359
Console-Beep -h 523 -p 24359 -n 24434
Pause-Console -p 24434 -n 24571
Console-Beep -h 932 -p 24571 -n 25753
Pause-Console -p 25753 -n 25771
Console-Beep -h 698 -p 25771 -n 26334
Pause-Console -p 26334 -n 26375
Console-Beep -h 622 -p 26375 -n 26475
Pause-Console -p 26475 -n 26571
Console-Beep -h 587 -p 26571 -n 26650
Pause-Console -p 26650 -n 26765
Console-Beep -h 622 -p 26765 -n 26831
Pause-Console -p 26831 -n 26971
Console-Beep -h 523 -p 26971 -n 28078

#Change your default color scheme back in your ISE

Notice how we don’t actually use hard coded values on the [console]::beep and Start-Sleep functions? The reason for this is that over time, the playhead for each individual console can get off just a tiny bit depending on CPU speed and the audio output buffer. These tiny delays add up over time and cause the consoles to get out of sync with each other. In order to get around this issue, we calculate the time based on its sync with the [Diagnostics.Stopwatch].

If our fixed playhead note start time isn’t activated until after the current stopwatch time, then we subtract the delay amount from the total note, allowing the notes to catch up.

Showing The PowerShell Command Window

You may be familiar with the fact that even when deploying a .ps1 in interactive mode, the PowerShell window will not be visible on the target machine. To get around this, we’re going to run an additional PowerShell process using the Start-Process command.

Start-Process powershell.exe -ArgumentList '-Command "& {
  Write-Output \"Hello from PDQ Deploy\"

Note the character escape on the quotations: \". Because we’re using the -Command "& { }" argument, all quotations need to be escaped.

On the PowerShell step, on the Options tab, set your Run As to Deploy User (Interactive)

Happy PowerShelling!

*2006 may not be that long ago, but in computer terms, we’re approaching ancient history.

Reading The Hosts File With PowerShell (local and remote)

2 CommentsPowerShell

Today’s blog focuses on reading the hosts file with PowerShell. Though there are many reasons that someone may modify the hosts file of a machine, it can easily get out of hand with entries scattered about your network.

Whatever the story, most of us have had reason to read or modify info in the hosts file at one point in our lives. We’re going to cover reading the hosts file with PowerShell locally as well as remotely for multiple machines. Yay!

Reading the hosts file with PowerShell

The hosts file is, simply put, a mapping of host names to corresponding IP addresses. You can go explore the how and the why of using it, but that’s outside the scope of this blog.

It’s easy enough to browse to the hosts file location and open the file manually. On modern versions of Windows, it’s stored here (you’ll need admin access to modify it):


But, what about keeping tally of all the machines on your network? Do you really want to manually browse to this file on each machine? This is most definitely a rhetorical question. The correct answer is an emphatic, “No!”

So, what do we do? We use PowerShell of course! Reading the hosts file with PowerShell is a breeze.

Getting the entire contents of hosts file with PowerShell

In order to do this, we are going to make use of the Get-Content cmdlet. This cmdlet will read the contents of a file.

If you only need to quickly see the entire contents of the hosts file, then this super simple one-liner should do the trick:

Get-Content $env:SystemRoot\System32\Drivers\etc\hosts

That’s simple enough and gets us the basic info that we’re looking for.

But, what if we want to only grab the actual entries instead of the entire file? Easy peasy with PowerShell.

Getting all entries from hosts file

In order to get all the entries from the hosts file, we’re going to use regular expressions to compare against every line in the hosts file.

Additionally, we will also log the output to a file. Having a log file will make it much easier to see all the results at a glance.

$LogFile = "\\MyScripts\Logs\hosts file results.csv"
$Pattern = '^(?<IP>\d{1,3}(\.\d{1,3}){3})\s+(?<Host>.+)$'
$File    = "$env:SystemDrive\Windows\System32\Drivers\etc\hosts"
$Entries = @()

(Get-Content -Path $File)  | ForEach-Object {
    If ($_ -match $Pattern) {
        $Entries += "$env:COMPUTERNAME,$($Matches.IP),$($Matches.Host)"

$Entries | Tee-Object -FilePath $LogFile -Append

Voila! It’s fairly simple and it gets you clean results with only the actual entries in the hosts file (rather than the entire file contents).

Fantastic! Now, let’s move on to doing this for all the machines!

Getting entries from hosts file for multiple machines

So, we’ve got this fancy script to get our hosts file entries and log them to a file. Getting them from multiple machines is also a snap.

Here are a few different ways that you can accomplish this:

  • PDQ Deploy and PowerShell steps
  • PDQ Inventory and Remote Command
  • Remote PowerShell and Invoke-Command
  • Directly access the files on the remote computers via UNC path


PDQ Deploy and PowerShell steps

Here are the steps to use PDQ Deploy with these PowerShell scripts.

  1. Create a PDQ Deploy Package with a PowerShell Step that includes your script.
  2. Deploy to your targets and verify your log file.

Like magic, you can now deploy to all your targets and save your results to a log file on some UNC path. Go you!

PDQ Inventory and Remote Command

Here are the steps to use PDQ Inventory with these PowerShell scripts.

  1. Select your targets and use Remote Command.
  2. Save your PowerShell script to a file and call that file from PowerShell Remote Command.
  3. Select all targets to see combined results. Computers with no results will simply show, “Return code: 0” since they successfully ran but didn’t have any results.

Ta da! It’s that easy. It’s very quick to select some machines and then see what they have in their hosts file.

Remote PowerShell and Invoke-Command

This requires that you have previously configured remote PowerShell for your targets. Otherwise, you’ll receive errors when attempting to connect to your targets. I’ve added Test-WSMan to filter those results.

Additionally, we’ll need to change how we’re logging to file otherwise we’ll run into double-hop authentication issues. 

$LogFile   = "\\MyScripts\Logs\hosts file results.csv"
$Result    = @()
$Computers = @("FRY", "UTER", "WOLVERINE")

$MyScript = {

    $Pattern = '^(?<IP>\d{1,3}(\.\d{1,3}){3})\s+(?<Host>.+)$'
    $File    = "$env:SystemDrive\Windows\System32\Drivers\etc\hosts"

    (Get-Content -Path $File)  | ForEach-Object {
        If ($_ -match $Pattern) {

Foreach ($Computer in $Computers) {

    If ( [bool](Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue)  ) {

        $Result += Invoke-Command -ComputerName $Computer -ScriptBlock $MyScript


$Result | Tee-Object -FilePath $LogFile -Append


Directly read remote hosts file via UNC path

This method is simply using the direct UNC path to the hosts files. In this example, I’m assuming the system drive is C.

$LogFile   = "\\MyScripts\Logs\hosts file results.csv"
$Pattern   = '^(?<IP>\d{1,3}(\.\d{1,3}){3})\s+(?<Host>.+)$'
$Entries   = @()
$Computers = @("FRY", "UTER", "WOLVERINE") 

Foreach ($Computer in $Computers) {

    $file = "\\$Computer\c$\Windows\System32\Drivers\etc\hosts"
    (Get-Content -Path $File) | ForEach-Object {
        If ($_ -match $Pattern) {
            $Entries += "$computer,$($Matches.IP),$($Matches.Host)"

$Entries | Tee-Object -FilePath $LogFile -Append

This will probably be the fastest method to get the hosts file info.

Wrapping Up

That’s it! Now, you have a bunch of ways to tackle the task of reading all your hosts file records.

These are just examples, so you’ll likely want to modify them for your environment. One change that I can think of, for example, is importing your list of computers. In my examples, I am using a static list of computers.

In any case, have fun with it!

Happy PowerShelling!


Escaping the Patch Management Cycle With Auto Deployment

Leave a commentAuto Deploy, PDQ Deploy

Do you enjoy repetitive tasks, busy work, and thoughtless clicking of buttons? If you do, then this blog might not be for you. This blog is for people looking for a better solution to patch management and software updates.

Are you staying late at work trying to update all of your machines to the latest version of Java, Flash, Chrome, etc? We feel ya. We’ve all been there.

Unless you’re a robot who enjoys repetitive tasks, everybody has better things to do than to worry about manual patch management and software updates. After all, those cans of Dr Pepper and Corgi videos aren’t going to enjoy themselves.

Well, today’s your lucky day, friend! PDQ Deploy has this fabulous feature called Auto Deployment that makes patch management and software updates an absolute snap!

Patch Management and Software Updates

We all have software that needs updating. If you’re managing computers, then you are probably well aware of the challenge of keeping all of your computers up-to-date.

The old inefficient way – one by one

Updating every computer one at a time is a very slow process. It’s prone to problems and sadness… but mostly sadness.

Here’s what this process typically looks like:

  • Finding and downloading the latest software
  • Physically going to a computer
    • Bonus points if you use a remote solution
  • Running the installer
  • Click clickity clicking through the installer
  • Waiting for the installer to finish
  • Crying a little bit on the inside
  • Rinse and repeating for every single computer

First of all, nobody likes this approach. Why? Because it’s full of hair-pulling and sadness. Unfortunately, this is common at many places.

The better way – Auto Deployment with PDQ Deploy

With the Auto Deployment approach, here is what patch management could look like:

  • Sitting back and relaxing
  • Learning PowerShell (check out our PowerShell blogs!)
  • Annoying your co-workers with paper airplanes
  • Drinking a beer (or two)
  • Smiling with the knowledge that you don’t have to manually install software for all of your computers!

I don’t know about you, but this process sounds much more appealing than the hair-pulling and eye-gouging that comes with manual software updates. Also, it takes all the busy work out and replaces it with valuable free time.

Sounds nice, right? So, let’s get on with the good stuff and show you how to set it up!

Patch Management Done Right – Auto Deployment with PDQ Deploy

So, what is Auto Deployment? Auto Deployment is a feature in PDQ Deploy that allows you to automatically deploy new versions of software packages as they become available in the PDQ Deploy Package Library.

We test these packages thoroughly to ensure that they install silently and are prepped for full automation. As a result, this means that you can rest assured that the software will work for you the first time. (Read more about the Package Library here – link)

Configuring Auto Deployment

Setting up Auto Deployment in PDQ Deploy consists of 3 easy steps:

  1. Create a Schedule
  2. Attach a Schedule to a Package in the Package Library
  3. Enjoy your new free time

So, pop open PDQ Deploy and follow along!

  1. Create a schedule

    • Click New Schedule in PDQ Deploy
      PDQ Deploy - New Schedule Button
    • Add a Schedule Name
      PDQ Deploy - Add Schedule Name
    • Add triggers
      PDQ Deploy - Schedules - Add Triggers
    • Add target computers
      PDQ Deploy - Schedules - Add Targets
  2. Attach a schedule to a package in the Package Library

    • Select Package Library.
      PDQ Deploy - Auto Deploy - Select Package Library
    • Select a Package.
      PDQ Deploy - Auto Deploy - Select a package
    • Select New Auto Deployment using existing schedule.
      PDQ Deploy - Auto Deploy - Add New Auto Deployment using existing schedule
    • Select your schedule and click OK.
      PDQ Deploy - Auto Deploy - Select Schedule and click OK
    • Now, your new Auto Deployment package will show up in its own fancy section. It will run according to the schedule and triggers that you defined.
      PDQ Deploy - Auto Deploy Page
  3. Enjoy your new free time

    • Of course, enjoying free time is up to you. Since you have just saved a lot of time and effort by using Auto Deployment, you should pat yourself on the back and go do something fun.

Extra things to consider

While this is just one simple example of an Auto Deployment setup, you may find that you have different needs. Because of this, we allow for many different configurations since everybody has different needs.

Here are some common questions we get about Auto Deployment packages:

Can I have multiple Auto Deployment packages in a single schedule? Yes!
You can attach a mixture of Auto Deployment packages and regular packages to a single schedule.

Can I install older versions of software? You bet!
Just like any package in the Package Library, you can choose older versions of software to install automatically.

Can I test Auto Deployment packages before deploying them to all of my machines? Of course!
With the Auto Deployment Approval Policy, you can set each Auto Deployment to approve updates immediately, manually, or on a schedule. It’s up to you and your needs.
This is configured in the Preferences.

PDQ Deploy - Preferences - Package Library

Can I modify the Auto Deployment package? Definitely!
We allow the ability to create pre-steps and post-steps within an Auto Deployment package. Just double-click your Auto Deployment package to open up the editor.

Auto Deploy - Google Chrome

Check out our PDQ Deploy Help Documentation for more detailed info.

Still not convinced? Watch a quick demo!

Wrapping Up

As you can see, we really turn it up to 11 with Auto Deployment for your patch management and software updates. So, do yourself a favor and try it out! Otherwise, you’ll kick yourself for not finding this solution sooner!

Simplify Your Scripting Life By Creating PowerShell ISE snippets

3 CommentsPowerShell

This post is dedicated to the forgetful. I have learned to cope with my forgetfulness by creating PowerShell ISE snippets.

I’ll be the first to admit that I tend to forget things. All the time. So much, in fact, that it may even seem intentional.

Does this sound familiar? Have you ever…

  • Opened up old scripts in order to remember exactly how you used a particular cmdlet?
  • Saved script templates so that you’d have an example of a particular cmdlet or syntax?
  • Ripped out your hair in frustration because you can’t find those old files or templates?!

Awesome. You’re human. We’ve all done that. Especially us forgetful folks.

Now, wouldn’t it be fabulous if there was a much simpler way to save your awesome syntax *and* your hair?

Well, you’ve come to the right place!

PowerShell ISE Snippets

When a problem comes along, yoooouuu must snippet.    ~ Devo. Maybe.

The PowerShell ISE has a slick little feature called snippets that you may or may not be familiar with! They were introduced way back in version 3 of the ISE.

You may already be using this feature without realizing it. Have you ever pressed Ctrl+J while using the ISE? Maybe you’ve right-clicked and opened up the context menu and selected Start Snippets. Both of these pop up the available snippets!

Go on! Try it out! All you need to do is select a snippet from what’s available.

At this point, you may be thinking, “That’s pretty nifty, but what good does that do me?”.

Lots of good, I say. Lots!

You will find that there are many default snippets that are available for you to use right out of the box. How thoughtful of the PowerShell team!

  • Need a Do While snippet? Done.
  • Need a Try/Catch snippet? Piece of Cake.
  • Need DSC template snippets? Fuhgeddaboudit!

Now, this is all fine and dandy, but what happens when you want something that’s simply not available? 

You pick up your mighty PowerShell hammer and create it!



Brace yourself. You’re going to love this. There is a cmdlet made specifically for creating PowerShell ISE snippets. *gasp!*

It’s called New-IseSnippet and it is wonderfully wonderful.

New-IseSnippet only has a few parameters, so it is really easy to get a new snippet created. Here is a list of those parameters.

-Title string (required)
This is what shows up in the snippets popup. It’s also the name of the snippet file.

-Description string (required)
This is what shows up in the description section of the snippets popup.

-Text string (required)
This is the PowerShell code that is actually added to your editor.

-Author string
This shows up in the snippet file itself rather than within the editor.

-CaretOffset Int32
This is the character offset for the position of the cursor after adding the snippet to your editor.

Here is where some of the parameters will show up within the snippet.

Now, enough with the talk. Less talk and more typing!


Creating PowerShell ISE Snippets with New-IseSnippet

Hands down, the hardest part of this entire process is coming up with a snippet that you want to create.

To start, let’s keep it simple and find something that you often use in your code. I often find myself using Test-Path in an If statement, so here’s an example snippet that I might create:

$Title       = "If Test-Path"
$Description = "If Test-Path valid do something. Or Do something else. You decide."
$Author      = "Kris"
$Text        = @'

If (Test-Path $SomePath) {

    # If Path valid

} Else {

    # If Path not valid


New-IseSnippet -Title $Title -Description $Description -Author $Author -Text $Text


Give that a spin. When you open up the snippets popup menu, you’ll see “If Test-Path” is an available snippet to use.

Now, when you select it, you should magically see If Test-Path appear.

Easy peasy. You’ve got this down. You’re pretty much a pro at this now.

So, let’s add another luscious layer to this delectable cake.


Creating PowerShell ISE Snippets with a PowerShell ISE Snippet

While on the subject of PowerShell ISE snippets, I decided to create a snippet that creates snippets, a little “snipception” if you will. Deep, I know.

Mostly, I wanted to give an example of using the -CaretOffset parameter and thought that this would be a fun example.

$Title       = "Snippet Template"
$Description = "'When a problem comes along, yooouu must snippet...' ~Devo. Maybe."
$Author      = "Mr. Snippet"

$Text = @'

$Title        = ""
 $Description = ""
 $Author      = ""
 $Text        = @"


New-IseSnippet -Title $Title -Description $Description -Author $Author -Text $Text

New-IseSnippet -Title $Title -Description $Description -Author $Author -Text $Text -CaretOffset 16 -Force


This will create a snippet called Snippet Template that you can use for creating even more snippets!

The -CaretOffset 16 part means that the cursor will start after the 16th character when the snippet is used.


Wrapping up

Well, that’s basically it. As you saw, it is fantastically fabulous. So, if you use the ISE, you should take a look at creating your own custom snippets. 

#Snippets4Ever #PowerShellLife or, however kids these days portray their excitement over snippets and PowerShell.

Happy PowerShelling!



Just in case you were wondering (and I’m sure you were), your newly created snippets will be stored in your user profile directory:


These are saved as XML files (.ps1xml files). That means that you can use all the powers of XML manipulation to create, modify, save, backup, and even cherish those files. Hurray!

Uninstall Skype for Business with OCT or Click-to-Run

Leave a commentUncategorized

The Powers From On-High have declared that no more shall Skype for Business be installed in your organization. No more recipe sharing, pictures of Ms. Coddlebottom’s trip to Aunt Bethany and Uncle Cleatus’ gator farm, or HR infractions in the form of wildly inappropriate memes involving kitchen utensils and spray foam. You have been tasked, and you will prevail.

Depending on which of the two methods you used to install Skype for Business, there are three methods of expungement. The first uses the Office Customization Tool (OCT), the second uses PDQ Inventory and C2R, and the third uses an XML and C2R.

If my math is right, that’s one for the OCT and two for the C2R. Below is an overview of the steps, but for a more in-depth dive, visit the Install/Uninstall Skype for Business article.

Uninstall using Office Customization Tool (OCT)

The ability to uninstall as a feature of Office 2016 is rather easy. The steps are the same as installing Skype for Business with the exception of the MSP. After an Office 2016 (OCT ) installation, follow these steps to install.

  1. With the exception of Set feature installation states, create another MSP the same way you would during an initial setup, as seen below:
    how to uninstall skype for business
  2. Save the MSP, the default name is SkypeUninstall.MSP.
  3. Create a PDQ Deploy package with a PowerShell step followed by an Install step. Both steps are necessary to prevent a failure, the PowerShell script is as follows:
    Get-Process "Lync*" | Stop-Process -Force
  4. Add the SkypeUninstall.MSP file to the Install step and watch as the magic that is PDQ Deploy adds the necessary parameters for you. Information on the Conditions and Options tab are included in the Deploy Office 2016 article.
  5. Save the package and deploy. It is recommended to test a deployment first.

Uninstall using PDQ Inventory and C2R

This method only applies to the C2R version of Skype for Business.

  1. In PDQ Inventory, navigate to the Applications page of the individual Computer window.
  2. Click on Skype for Business 2016. Copy the uninstall string listed in the Uninstall column, as seen below:
    "C:\Program Files\Common Files\Microsoft Shared\ClickToRun\OfficeClickToRun.exe" scenario=install scenariosubtype=ARP sourcetype=None productstoremove=SkypeforBusinessRetail.16_en-us_x-none culture=en-us version.16=16.0
  3. Create a new PDQ Deploy package with a Command step.
  4. Paste the uninstall string into the Command and delete the last two statements, culture=en-us version.16=16.0. The string should now look as follows:
    "C:\Program Files\Common Files\Microsoft Shared\ClickToRun\OfficeClickToRun.exe" scenario=install scenariosubtype=ARP sourcetype=None productstoremove=SkypeforBusinessRetail.16_en-us_x-none
  5. Save the package and deploy. As usual, we recommend you test this first.

Uninstall using an XML and C2R

This method only applies to the C2R version of Skype for Business.

  1. From Microsoft’s site, download the Office 2016 Deployment Tool (C2R).
  2. Create a shared directory for the C2R/Skype for Business “deployment”: \\server\share\
  3. Install the Office 2016 Deployment Tool for C2R and select \\server\share\ as the extraction location.
  4. The deployment tool will create configuration.xml, copy this to SkypeRemove.xml in the same directory.
  5. Edit SkypeRemove.xml. The XML should look similar to this (change the Language ID and Product ID as appropriate):
       <Product ID="SkypeforBusinessRetail" >
        <Language ID="en-us" />
     <Display Level="None" />
  6. Create a new PDQ Deploy package with a Command Step. Modify to match your \\server\share choices from above:
    "\\server\share\setup.exe" /configure "\\server\share\SkypeRemove.xml"

    On the Options tab, select the Run As option to Deploy User (Interactive)

  7. Save the package and deploy.

Notes and Addenda of Some Import:

  • Office 2016 Deployment Tool (C2R) supports these Product IDs.
  • It is recommended using the Run As option Deploy User (interactive) when uninstalling (and installing) Office products,
  • The products listed are for United States English (en-us). For use with other languages, please see the Microsoft Language Identifiers site.
  • The process for uninstalling Skype for Business 2013 is similar to 2016. You will need to download the Office 2013 Deployment Tool as well as use the correct Product ID for the 2013 version, which is either LyncRetail or LyncEntryRetail.
  • Error 17002. This error occurs if the Lync.exe process is still running when an uninstall is attempted, specifically the Skype for Business feature (OCT) uninstall.
  • Make certain you are uninstalling the appropriate product as expressed by the Product ID defined by Microsoft. Skype for Business has two Office 2016 options: SkypeforBusinessRetail and SkypeforBusinessEntryRetail.

See Also…
Deploy Office 2016 Using The Microsoft Office Customization Tool
Deploy Office 365
Silently Installing Office 365
Admin Arsenal Live! : Uninstalling Microsoft Office with PDQ Deploy
Admin Arsenal Live! : Adding and Removing Microsoft Office Components
Configuring and Deploying the Microsoft Office Customization Toolkit
Preparing The Office Customization Toolkit For Deployment
Deploy the Office Customization Toolkit Remotely

Enforcing Execution of Browser Extensions Explicitly – Part II

Leave a commentUncategorized

Part the Second: Internet Explorer

A few weeks ago, we posted about how to manage Chrome Extensions. This week, we’re doing the same for Internet Explorer (IE), the other enterprise browser. And like Chrome, we’re using the best tool for the job, Group Policy. (See the note at the end of this blog for information about IE’s future).

Let’s get to it.

Add-on Management for Internet Explorer:

The first thing you want to do is set up Internet Explorer the way you want it set for your users. This means all the add-ons they could possibly need should be included. You can add some of the pre-built add-ons from the IE Gallery. You’ll notice, however, the Gallery is a lot smaller than it used to be and certain things no longer work. 404 Page Cannot Be Displayed. Sad! Other add-ons can be included by deploying programs with IE add-ons included (e.g. Adobe Reader, Adobe Acrobat, Office, Java, etc.)

Once you’ve selected all the add-ons, go to manage add-ons in IE, right-click one of the add-ons and select More Information. You’re going to need the Class ID for the GPO. Copy the More Information into Notepad or other text editor so you can copy the Class ID.

Repeat this process for all the add-ons you want to manage in IE.

Now create the GPO.

Internet Explorer Add-on management should already be included in Group Policy. Open the GPMC, create a new Group Policy, edit that policy and navigate to Computer Configuration > Policies > Administrative Templates > Windows Components > Internet Explorer > Security Features > Add-on Management. You should see something like this:

We’re going to set up the Add-on List. Open that setting, enable it, and click Show… next to Add-on List. Add your Class IDs you retrieved earlier and choose the value you want. The values are as follows:

0 = The add-on is disabled and cannot be changed

1 = The add-on is enabled and cannot be changed.

2 = The add-on is enabled and can be changed.

Here’s an example of the add-ons with values for the Add-on List setting:

The next thing is to deny all the add-ons not defined in the Add-on List. It’s pretty straightforward. Open the Deny all add-ons unless specifically allowed in the Add-on List and enable the setting.

That’s it. Save the GPO, apply it wherever makes sense, and you’re finished.

Next, let’s run some tests.

First, we deploy the Shockwave Player and Java to a machine with IE installed. We see some add-ons in the Manage Add-ons properties of IE.

Next, we wait or run gpupdate on the target machine, restart IE. Go to Manage Add-ons again, and voila:

Next, we’ll take a look at Bethany Coddlebottom’s machine (the GPO has not been applied)…

Move that machine into range of the GPO, run gpupdate, and restart IE:

That’s it.


As you likely know, on January 12, 2016, support for older IE versions ended. Security updates would still be provided for the latest supported version through the OS lifecycle, but no additional IE releases were planned. You can read more here.

Introducing PDQ Deploy Throwback

Leave a commentUncategorized

Right now you can head to your local store and pick up a six-pack of Crystal Pepsi. Turn on Netflix to see reboots of X-Files, and Full House, and coming soon to a theater near you: Power Rangers, and Baywatch… respectively. Although it doesn’t take much to imagine a crossover between the two: “Mighty Morphin’ Power Baywatchers”… The 90’s are back baby! Why am I bringing this up? Well we’re jumping on this pre-millennium bandwagon like the sellouts we strive to be.


PDQ Deploy distributed on thirty (30) 3.5″ floppy disks for your install pleasure (or suffering, whatever you’re into)

Tech is going retro, and so should you. Order your set of floppies, and we’ll have them sent 90’s style… by carrier pigeon! That’s right, the dream of the 1890s is alive at PDQ.com.

Some Restrictions Apply, not available in all States, Territories, or Colonies. Void where prohibited or construed as embarrassing. Please allow 4 to 6 years for delivery.

Look how they stack!


Happy April Fools' Eve PDQers!

Adding and Removing Windows 10 Apps with PowerShell

Leave a commentPowerShell, Uncategorized

Raise your hand if you’ve ever had problems removing Windows 10 Apps. Raise your other hand if you’re fancy enough to know these by their name, Universal Windows Platform apps.

If you’ve got both hands up, clap them together. You’re fancy!

Generally speaking, removing Windows 10 Apps with PowerShell can be tricky. Once you get the hang of it, however, you may find it surprisingly straightforward.

In contrast to the fire hose blast of info in our webcast about Windows 10 Apps and Advertising (link here), I’m going to keep this blog relatively simple and code-focused.

Before we begin, there are some important things to note:

  • Remove-AppxPackage only removes apps for the current user
  • Remove-AppxProvisionedPackage doesn’t uninstall the app, it removes the provisioned package from Windows
  • Removing provisioned packages prevents new users from receving a Windows App
  • All glory to the hypnotoad

Also, there is no time for raised hands, so put your hands down and let’s get typing!

Removing Windows 10 Apps

You have before you the PowerShell gold that is removing Windows 10 apps. These commands will help with removing Windows 10 apps in a very simple and straightforward way.

Get installed Windows 10 Apps


Get Installed Windows 10 Apps by Name
Get-AppxPackage *weather*


Get Installed Windows 10 Apps for all users
Get-AppxPackage -AllUsers


Get Installed Windows 10 Apps by Name for all users
Get-AppxPackage *weather* -AllUsers


Removing Windows 10 Apps by name
Get-AppxPackage *weather* | Remove-AppxPackage


Removing Windows 10 Apps by list
$AppList = @(



foreach ($App in $AppList) {

    Get-AppxPackage -Name $App | Remove-AppxPackage

Removing Windows 10 Apps with an exception list
$ExceptionList = @(



Get-AppxPackage | Where { $App = $_; -not ( $ExceptionList | Where-Object { $App -like $_ } ) } | Remove-AppxPackage

Adding Windows 10 Apps

Now that we’ve mastered removing Windows 10 Apps, this is how we add Windows 10 Apps back. That is, if you ever decide to walk that lonely road.

Add Windows 10 App from Manifest
$AppName = "Weather"
$AppPath = @()
$AppPath += Get-ChildItem 'C:\Program Files\WindowsApps\' -Filter "*$AppName*x86*" | Select -ExpandProperty FullName
$AppPath += Get-ChildItem 'C:\Program Files\WindowsApps\' -Filter "*$AppName*x64*" | Select -ExpandProperty FullName

Add-AppxPackage -DisableDevelopmentMode -Register "$AppPath\AppxManifest.xml"


Add Windows 10 App from Provisioned Apps List
$ProvisionedList = (Get-AppxProvisionedPackage -Online).DisplayName
$AppPaths = @()
foreach ($App in $ProvisionedList) {

    $AppPaths += Get-ChildItem 'C:\Program Files\WindowsApps' -Filter "$App*x86*" | Select -ExpandProperty FullName
    $AppPaths += Get-ChildItem 'C:\Program Files\WindowsApps' -Filter "$App*x64*" | Select -ExpandProperty FullName


$AppPaths | ForEach-Object { 

    Add-AppxPackage -DisableDevelopmentMode -Register "$_\AppxManifest.xml" 



Removing Windows 10 App Provisions

While removing Windows 10 Apps is important, we need to prevent them from being added in the first place. Using the following code, you’ll be able to do just that.


Get Provisioned Windows 10 Apps
Get-AppxProvisionedPackage -Online


Get Provisioned Windows 10 Apps by Name
Get-AppxProvisionedPackage -Online | Where-Object {$_.DisplayName -like "*weather*"}


Removing Provisioned Windows 10 Apps by List
$AppList = @(



foreach ($App in $AppList) {

    Get-AppxProvisionedPackage -Online | Where-Object {$_.DisplayName -like $App} | Remove-AppxProvisionedPackage -Online



Things to keep in mind

Even though I said this at the start of the blog, it’s worth saying again, removing Windows 10 Apps happens per user

As of this moment, there is not a way to directly remove Windows 10 Apps for another user. As a result, there are a lot of blogs and forum posts on the internet with info that is wrong.

Yep. I said it. They’re wrong. Who could have ever guessed?! People are actually wrong on the internet!

Simply put, it’s because the Remove-AppxPackage cmdlet only works for the current user.


To sum it up, the above code will not remove SomePackage for all users. In fact, it will only remove the package from the current user.

Since we’re not doing a deep dive today, I’ll spare you the fancy details. But, if you’re curious, you’ll find a RemovePackageAsync method on the PackageManager class description page. That’s what Remove-AppxPackage uses.

In any case, this means you’ll have to use another method to get the apps removed from users who are not currently logged on. Here are a few ideas that we’ve come up with:

  • Putting a script in the startup folder (check out Jordan’s blog post)
  • Using Scheduled tasks
  • Using PDQ Deploy
  • Using Logon Scripts

Also, if you’re a fan of the DESTROY EVERYTHING approach, you may run into some issues. For example, Cortana and Edge browser are considered core apps and cannot be removed. So, you’ll have to plan ahead for what you want to remove.

In any case, have fun destroying all the things.

Happy PowerShelling!

Install Skype for Business with the OCT or Click-to-Run

Leave a commentDeployment Examples, PDQ Deploy

Tasked as you are to install Skype for Business so users can chit chat all the livelong day and collaboratively interface about marketing buzzwords, cotillions, bar mitzvahs, the annual Potato Festival, and probably work too, we have this fair post to help in your Skype for Business deployment adventure. If you are looking to uninstall Skype, we have a blog post for that too.

Using PDQ Deploy, there are two methods for deploying Skype for Business. One method is using the Office Customization Tool and the second is using the Office Deployment Tool (AKA Click-to-Run or C2R).

Install Skype for Business with the Office Customization Tool (OCT)

If you’re performing a fresh install of Office 2016 using the OCT, please see the Deploy Office 2016 KB. The option to install Skype for Business is included in the Office 2016 OCT configuration.

If you have already installed Office 2016 using the OCT and receive the Skype for Business quest, follow this handy walkthrough.

  1. Create another MSP using the same parameters except for the Set feature and installation states. Change the features as seen below:skype features and parameters
  2. Save the MSP. In our example, the MSP is saved as SkypeUpdate.MSP.
  3. Create a package in PDQ Deploy and add an Install step. Select the MSP file in the Install File field using the File Picker […]. The necessary parameters are added for you automatically because we are beings of light and magic. For more information on how to create the package with Conditions and Options, please see the Deploy Office 2016 KBsilently install skype for business
  4. Finally, complete your quest by deploying the package to computers with Office 2016 installed but missing the Skype for Business feature. Of course, we recommend testing first.

Install Skype for Business using Click-to-Run (C2R)

Don’t be daunted by the number of steps in this walk through. There’s no heavy lifting required, and it’s far easier to use C2R to install Skype for Business than it is to make macaroni and cheese from a box.

  1. Download the Office 2016 Deployment Tool (for C2R deployments) from Microsoft’s site.
  2. Create the necessary shared directory to serve the C2R/Skype for Business deployment: \\server\share\ (make sure the Deploy user has full share-level and NTFS permissions for the shared directory).
  3. Install the Office 2016 Deployment Tool for Click-to-Run, choose the extraction location to the previously created \\server\share\ .
  4. Copy the configuration.xml (installed with the Deployment Tool) to SkypeDownload.xml, or another name of your choosing, in the same directory.
  5. Now edit SkypeDownload.xml. Change OfficeClientEdition to “64” if you’re deploying the 64-bit rather than 32-bit version of Skype. Change the UNC of SourcePath to match the directory created in Step 2 above. Your XML should look something quite close to this:
     <Add SourcePath="\\server\share\" OfficeClientEdition="32">
     <Product ID="SkypeforBusinessRetail">
     <Language ID="en-us" />
  6. Once you’ve saved the XML, open a command prompt, modify, and then run the following command (modifications may include using quotes if your paths contain spaces and changing SkypeDownload.xml if you used a different XML file name):
    "\\server\share\setup.exe" /download "\\server\share\SkypeDownload.xml"skype command prompt

    Wait for the download to complete. Grab a whiskey, commit to another shorter quest, or play Minesweeper. The files will be downloaded in an automatically generated directory called \\server\share\Office (you should not, and need not, create this directory beforehand). Once the download has completed, you will be returned to a prompt.

  7. Next, make another copy of the configuration.xml and name it something meaningful, like SkypeConfig.xml.
  8. Now edit SkypeConfig.xml and mirror any changes made in the step above (e.g. edition, branch). Your XML should look similar to this:
     <Add OfficeClientEdition="32" Branch="Current">
     <Product ID="SkypeforBusinessRetail" > 
     <Language ID="en-us" /> 
    <Updates Enabled="TRUE" Branch="Current" />
    <Display Level="None" AcceptEULA="TRUE" />
  9. Now we create the package. In PDQ Deploy, create a new deployment and name it something meaningful.
  10. Next, add a Command step and modify to match your choices from above:
     "\\server\share\setup.exe" /configure "\\server\share\SkypeConfig.xml"skype config deploy

    Set the deployment Run As option to Deploy User (Interactive).deploy interactive skype for business

  11. Finally, save the package and then test the deployment (Right-click the completed package and select Deploy Once). If the results are acceptable, deploy at will and your quest is complete.



Notes and Addenda of Some Import:

  • The Office 2016 Deployment Tool (C2R) can be downloaded from Microsoft’s Site.
  • The Office 2016 Deployment Tool (C2R) supports these Product IDs.
  • The XML tag, <Property Name=”AUTOACTIVATE” Value=”0″ />, is not supported for Office 365 ProPlus products.
  • When installing Office products, it is always a good idea to deploy using the Run As option Deploy User (interactive).
  • The products listed in this article are for United States English (en-us). For use with other languages, please see the Microsoft Language Identifiers site.
  • The process for installing Skype for Business 2013 is similar to 2016. You will need to download the Office 2013 Deployment Tool as well as use the correct Product ID for the 2013 version, which is either LyncRetail or LyncEntryRetail.


  • Error 1603. This error occurs most frequently when Skype for Business is either already installed or a version of Office exists that is not compatible with the version of Skype for Business you are installing.
  • Make certain you are installing the appropriate product as expressed by the Product ID defined by Microsoft. Skype for Business has two Office 2016 options: SkypeforBusinessRetail and SkypeforBusinessEntryRetail.

See Also…
Deploy Office 2016 Using The Microsoft Office Customization Tool
Deploy Office 365
Silently Installing Office 365
Admin Arsenal Live! : Uninstalling Microsoft Office with PDQ Deploy
Admin Arsenal Live! : Adding and Removing Microsoft Office Components
Configuring and Deploying the Microsoft Office Customization Toolkit
Preparing The Office Customization Toolkit For Deployment
Deploy the Office Customization Toolkit Remotely
Product IDs that are supported by the Office Deployment Tool for Click-to-Run
Office 2016 Deployment Tool
Office 2013 Deployment Tool
Language Identifiers

Registry Scanner

Leave a commentPDQ Inventory, Scanning

If you’re looking for an easier way to scan the registry of your computers, you are at the right place. There are just a few steps to take to set up a Scan Profile with a registry scanner to find what you are looking for. Watch a video tutorial, or follow the steps below.

Setting Up Your Registry Scanner

  1. Go to File > Preferences > Scan Profile. Here you can create a new Scan Profile (Click New), or alternatively, you can add a registry scanner to an existing profile (Highlight a Scan Profile and click Edit). scan profile preferences
  2. Click Add > Registry to begin building your registry scanner. Note the available wildcards you can use to narrow down your search. RegistryScannerEdit
  3. Click OK to add the registry scanner to your selected profile. If you’re done adding scanners click OK again to return to the Preferences screen. Registry scanner - RegistryProfileExample
  4. Now you’re ready to use your newly updated or created Scan Profile. After exiting out of Preferences, in the main window click the Scan icon and select your Scan Profile to run against your selected computers. (In this example, a new Scan Profile was created and named Misc. Registry. Remember you can add a registry scanner to any existing Scan Profile or build a Scan Profile with multiple scanners in it.) Run New Registry Profile

Viewing Your Registry Scanner Results

Now that you’ve got your Scan Profile set up and have run it against your computers, now it’s time to create a report. In the top toolbar you’ll see a drop down that allows you to create a new report.

The example below shows the filters for a report that shows computers that have a 32-bit version of Chrome. Obviously, depending on what you are looking for in the registry your report will look quite different and require different filters. To master using filters in reports, we highly recommend you watch this playlist on using filters or you can also watch our recent webcast on Advanced Reporting in PDQ Inventory.

Google Report 32 bit