Pinging Faster with Test-Connection

Pinging in PowerShell is easy, but it always seems to be slow. As an example, here’s a command to ping 254 addresses:


Six minutes! Part of the issue here is that we’re pinging these hosts one by one in serial, meaning that we have to wait for the previous command to finish before we can begin the next one.

Luckily Test-Connection has a parameter AsJob that allows us to run each test more or less at the same time. The script here is a bit more complicated as we now have to deal with the management of the jobs1 (a new job is created for each IP we’re testing). We’re also using Select-Object here because there seems to be some odd performance hit when Test-Connection is allowed to output normally. Consequently we’re also using Where-Object to return only those hosts which we were able to ping successfully (for more information on ping status codes, check out the StatusCode section in the Win32_PingStatus2 documentation):

1..254 | ForEach-Object {
    Test-Connection -ComputerName "192.168.1.$_" -Count 1 -ErrorAction:SilentlyContinue -AsJob
} | Get-Job | Receive-Job -Wait -AutoRemoveJob | Where-Object {$_.StatusCode -eq 0} | Select-Object -Property Address

And the results…


Two seconds! That’s more like it! Kudos to Anders Wahlqvist3 over on StackExchange for providing the code neccessary to accomplish this.


Finding Misconfigured Calendar Permissions in Office365

We recently had an issue at work where someone discovered that their calendar could be viewed by another user in the organization that they didnt give explicit permission to. Upon further inspection of the problem, we discovered it was being caused by the exchange role of Reviewer1 being given to the Default user on the user’s calendar folder. The Default user in exchange refers to any user in the organization, so any permissions assigned to it is essentially saying “I want every user in my company to be able to do this to this resource2. We fixed the user’s issue quickly by modifying the permissions, but I started wondering if anyone else had accidentally modified the Default user on their calendar as well. Powershell time! I created a script to check the Default user permissions on each user’s Calendar folder and return any calendars where it wasn’t set to the company default of AvailabilityOnly.

$UserCredential = Get-Credential
Connect-MsolService -Credential $UserCredential

$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri -Credential $UserCredential -Authentication Basic -AllowRedirection
Import-PSSession $Session

$StandardPermission = "AvailabilityOnly"

$inboxes = Get-Mailbox -ResultSize Unlimited | Select-Object -ExpandProperty UserPrincipalName
$inboxpercent = (100 / $inboxes.Count)
$i = 0
foreach($inbox in $inboxes){
    $i = $i + $inboxpercent
    Write-Progress -Activity "Checking permissions" -Status "Checking $inbox" -PercentComplete $i
    $inboxperm = Get-MailboxFolderPermission -Identity $inbox`:\Calendar -User Default
    if ($inboxperm.AccessRights -ne $StandardPermission) {
        $inboxperm | Add-Member -NotePropertyName Inbox -NotePropertyValue $inbox -PassThru | Select-Object FolderName,User,AccessRights,Inbox

It takes a while to traverse through all the users, but the script will output it’s progress as it plods along using the Write-Progress cmdlet. I haven’t tested it, but the script should also be able to work on newer versions of Exchange Server as well (just remove the part where it connects to Office365, right above the line $StandardPermission = "AvailabilityOnly"). If you’re interested in more information about calendar permissions in Office365, check out the following article from Microsoft:

  2. Most companies set the default for Default on calendars to something like AvailabilityOnly or LimitedDetails, which lets users view free and busy time in other users calendars to help in scheduling meetings, but nothing else.

Hunting Down Rogue Static IPs

I recently ran into an issue at work where someone had set static IPs on a handful of nodes as a temporary fix, but then forgot to exclude the IPs on our DHCP server. This was in turn causing issues with some other nodes that were getting their IPs from the DHCP server dynamically, and were then receiving a DHCP NAK on startup when their assigned address was already in use. With the problem identified, I decided to hunt down these misconfigurations by determining which IPs on our DHCP server should not be in use, and then simply ping them. Determing the IPs that shouldn’t be in use is bit complicated though, as you have to take the IP range you want to query and then remove all leases, reservations, and manual exclusions from it. Thankfully Microsoft provides a powershell cmdlet that does all that work for us, Get-DhcpServerv4FreeIPAddress (phew!). With that cmdlet at hand, we can now take those IPs1 and pipe them out to Test-Connection. Any results that are returned are IPs that the DHCP server believes to be free, but are actually in use as an undocumented static IP.

$dhcpserver = ""
Get-DhcpServerv4Scope -ComputerName $dhcpserver | Get-DhcpServerv4FreeIPAddress -ComputerName $dhcpserver -NumAddress 1024 |
ForEach-Object {
    Test-Connection -ComputerName $_ -Count 1 -ErrorAction:SilentlyContinue

By default the script will query all scopes, but if you want to get more granular and only target one scope, in the Get-DhcpServerv4Scope command add on the -ScopeID parameter followed by the ID of the scope you want to target.

If you have alot of IPs that you want to query, or just want to monitor the script’s progress, I have a fancier version of this script too.

$dhcpserver = ""
$ips = Get-DhcpServerv4Scope -ComputerName $dhcpserver | Get-DhcpServerv4FreeIPAddress -ComputerName $dhcpserver -NumAddress 1024 -WarningAction:SilentlyContinue
$percent = ( 100 / $ips.Count )
$i = $percent
foreach($ip in $ips){
    Write-Progress -Activity "Testing Connections" -Status "Pinging $ip" -PercentComplete $i
    Test-Connection -ComputerName $ip -Count 1 -ErrorAction:SilentlyContinue
    $i = $i + $percent


  1. Get-DhcpServerv4FreeIPAddress can only return a maximum of 1024 addresses per scope. If any scope on your DHCP server has more than 1024 possible addresses in it, be aware that this script may not be able to test them all.

Indenting Multiple Lines in Vim

I’ve been working with Ansible of late, and just like Python it’s very finicky about indentation. When modifying Ansible playbooks in Vim I found the need to be able to indent blocks of code all at once. Vim to the rescue! Normally to indent1 one line in Vim you would put the cursor anywhere on the line that you’d like to indent and hit >​ (with < used to unindent). Now to indent multiple lines we can leverage Vim’s visual mode to select more than one line. Put the cursor on the first line you would like to indent and then type v to enter into visual mode. From here move the cursor down to the last line you would like to indent and press >.


Want to indent the same block of code twice? You may have noticed by now that once you send your indent command you’re then exited out of visual mode, meaning you’ll have to select which text you want to indent all over again. After you’ve used visual mode once, type ​gv and you you’ll re-enter it with your previous selection already highlighted!


  1. Indenting is set to 8 spaces by default, but this can be changed by modifying the shiftwidth setting in Vim. It can be changed on the fly to 4 by running :set sw=4 in Vim, or permanently by adding the line set shiftwidth=4 into your .vimrc file in your home folder.

Set-ADUserLogonTo PowerShell Module

At work I sometimes have to set account restrictions up on an account to limit the user to only be able to logon to certain PC’s. Usually this is to restrict the account to only being able to logon to certain computer labs. In the past I would accomplish this by opening up ADUC, clicking on the accounts LogonWorkstation dialog box, and then manually entering each computer that the account needs to logon to, often with the computernames being almost identical(the computers were named after the room number they were located in). I got a bit tired of doing this after a few times, so I started to look into setting the field via PowerShell. The Set-ADUser command lets you set the field by using the -LogonWorkstations parameter, but you have to provide a comma separated list with each host you want the account to be able to logon to. I wanted wildcard support! So to fix the problem, I created a PowerShell module that gave me the wildcard support, Set-ADUserLogonTo. In it are two functions, Get-ADUserLogonTo and Set-ADUserLogonTo. Get-ADUserLogonTo can be used to either return which computers the account is restricted to logging into, or just how many computers are in that list. Set-ADUserLogonTo can be used to either set the restrictions on what the account can log into(click here to get some examples as to how the wildcard support works), or just to remove the restrictions, allowing the account to logon anywhere. If you’d like to install it, check out the project’s README on it’s project page here.


I’ve added a new function to my MDTApplicationTools PowerShell module, called Rename-MDTApplication. The function renames MDT applications, with an optional parameter to rename the applications source directory as well.

Renaming an application via MDT’s GUI usually is pretty easy, so why the function? While renaming an application in MDT works OK, it was missing a bit of functionality that I wanted, which was to also rename the application’s source directory. Via the GUI if I were to rename APP1 to APP2,the name would change to APP2 in MDT, but the source and workingdirectory would still point to .\Applications\APP1. Now with this function, I can safely go back and rename my applications, knowing that my source directories will match their application names.

If you’d like to install the module, head over to MDTApplicationTools project page and checkout the README. If you already have the module installed (and are also running PowerShell 5), just run Update-Module -Name MDTApplicationTools to get the latest version.


MDTApplicationTools PowerShell Module

Earlier in the year I took charge of my work’s Windows 7 to Windows 10 Migration. I work at a college, and a good part of the machines we manage are computer labs, for which we have to manage about 130 different software packages (I should probably mention at this point that we use the Microsoft Deployment Toolkit to manage all of our images, drivers, and packages). As part of the migration, I wanted to mark all packages in the labs as supporting Windows 7 Only in MDT, and then gradually bring in each package, testing to make sure their installation, settings configuration, and user experience didn’t break in Windows 10, and afterwards removing the OS restriction once I had verified the app was OK. I also wanted a way to query a package that is dependency of a bunch of other packages, so I could then test all those dependent applications to make sure my updating their shared dependency didn’t in turn break those packages themselves(I had one package in mind here for the shared dependency issue. It’s name rhymes with lava)

Through having the Microsoft Deployment Toolkit installed on my computer I had access to all the MDT PowerShell Cmdlets, and could mount my MDT shares as a drive in PowerShell (cool stuff), but I didn’t really have the fine grain control I wanted in order query and manipulate the MDT applications in this manner. So what would be the shortest, easiest, most efficient way to get around these problems? Spend hours upon hours creating a PowerShell module of course! Here’s an excerpt from the README file to give you an idea of what it does:

  • Get-MDTApplication
    • Retrieves MDT applications, either by name/guid, or just all of them.
  • Get-MDTApplicationDependency
    • Retrieves either the parent or child dependencies of an MDT application. Can either return one depth, or can recurse the entire depth of the dependency tree
  • Get-MDTApplicationSupportedPlatform
    • Queries MDT applications to see if they have the SupportedPlatform attribute set.
  • Set-MDTApplicationSupportedPlatform
    • Sets the SupportedPlatform attribute on MDT applications
  • Find-MDTApplicationContent
    • Searches through either the installcmd attribute or the install scripts themselves of all the MDT applications in a share for the specified string(i.e “pause”, “powershell.exe”, “C:\Program Files (x86)”, etc.)

It took me a while to make, but I learned a ton in the process, and in the end I have a tool that helps me out a great deal when updating or changing applications. If you’d like to learn more, check out the project’s page below. The README file on that page should have all the info you need to get it up and running on your computer.