List email addresses for ActiveSync device associations in Office 365

Today I had to get a list of email addresses for iOS users associated with a specific Office 365 tenant.

Using the Get-MobileDevice cmdlet in Exchange/Office 365 PowerShell is handy, but it only shows the user’s name, not their email address:
image

There are some older solutions around on the web that involve running a Get-Mailbox, and then iterating through each mailbox to get the ActiveSync device information. This seemed like overkill to me, as I only needed a basic list.

I ended up with the following one-liner, which uses Calculated Properties to grab the email address:

Get-MobileDevice -ResultSize Unlimited | Select-Object @{Name='User';Expression={(Get-Mailbox -Identity $_.UserDisplayName) | Select-Object -expand WindowsEmailAddress}},DeviceID,DeviceImei,DeviceOS,DeviceType,DeviceUserAgent,DeviceModel | Export-Csv C:\temp\mobile_devices.csv

This allowed me to open the CSV in Excel and filter down the list until I was left with the information that we were after.

Your mileage may vary using this command, as we’re matching a ‘UserDisplayName’ field on a Microsoft.Exchange.Data.Directory.SystemConfiguration.MobileDevice to the ‘Identity’ field on a Microsoft.Exchange.Data.Directory.Management.Mailbox.

Create a Scheduled Task to keep all Chocolatey Packages up-to-date

Rebuilding your PC is always a drag, even with useful utilities like Ninite.

I recently created a PowerShell DSC script that I can use whenever I need to rebuild my PC. As part of that, I used the cChoco provider to automatically install applications using Chocolatey. I’ll be writing a blog post with more details shortly.

That’s a great way to get the applications installed, but not for keeping them up-to-date. Chocolatey allows you to run ‘choco upgrade all’ manually to do this:

Command prompt window showing the results of a 'choco upgrade all -y' command

Rather than manually create the scheduled task to automate this, I created this short PowerShell script:

The script will:

  1. Locate the choco.exe binary (It’ll quit if it can’t find it in the path)
  2. Set up a scheduled task that runs said binary at system startup

Note that this script will only work on Windows 8 and newer machines, because it relies on the *-ScheduledTask cmdlets.

Use PowerShell to list all Windows Performance Counters and their numeric IDs

I’ve been looking into using Zabbix again lately, and was delighted to see that they’ve now released a refreshed and improved version 3.0

Whilst I was browsing some of my old Chrome bookmarks for Zabbix, I came across their article about Windows performance counters. They mention that performance counters in Windows have localised names, and that if you’re running systems in several different languages, you need to refer to the counters by their ID, rather than by name, when configuring monitoring.

This can be achieved by looking the list up in the registry, but it’s all just stored as a multi-line string value:
image

image

This isn’t ideal if you’re searching for something specific, or you just want a nicely-formatted output. I’ve created a quick PowerShell function that outputs these values, and allows you to use standard PowerShell operations to search and filter.

For example, output to a text file:

Get-PerformanceCounter | Out-File c:\temp\perfcounters.txt

Results in this:
image

Or you could do something like this:

Get-PerformanceCounter | Where-Object {$_.CounterName -like 'file write*'}

Which results in this:

CounterID CounterName              
--------- -----------              
       12 File Write Operations/sec
       18 File Write Bytes/sec

Or, you could sort them by name:

Get-PerformanceCounter | Sort-Object -Property 'CounterName' | Format-Table -AutoSize

Which would result in the following output

CounterID CounterName                                                   
--------- -----------                                                   
     7024 # Bytes in all Heaps                                          
    10686 # exceptions                                                  
     7014 # GC Handles                                                  
     6986 # Gen 0 Collections                                           
     6988 # Gen 1 Collections                                           
     6990 # Gen 2 Collections                                           
    10688 # images                                                      
     7018 # Induced GC                                                  
     7124 # Link Time Checks                                            
     7086 # of CCWs                                                     
     7108 # of current logical Threads                                  
     7110 # of current physical Threads                                 
     7112 # of current recognized threads                               
     7150 # of Exceps Thrown                                            
     7152 # of Exceps Thrown / sec                                      
     5046 # of failed workflow jobs                                     
     5048 # of failed workflow jobs/sec                                 
     7154 # of Filters / sec                                            
     7156 # of Finallys / sec                                           
     7072 # of IL Bytes Jitted                                          
     7090 # of marshalling                                              
     7070 # of Methods Jitted                                           
     7030 # of Pinned Objects                                           
     5050 # of resumed workflow jobs                                    
     5052 # of resumed workflow jobs/sec                                
     5054 # of running workflow jobs                                    
     5056 # of running workflow jobs / sec                              
     7032 # of Sink Blocks in use          

Here’s the code:

Random PowerShell: Determine the longest job title in AD

This one’s kind of random, and a real edge case, but it shows how handy a bit of PowerShell knowledge is to quickly dig information out of Active Directory.

As part of a email signature redesign, we’d received a new HTML template from our web designers. Whilst testing out the template, I ran into an issue with text wrapping around if the user had a long job title. I needed to get back to the designers with an example of the longest job title we’d be likely to see in our environment.

Here’s how I discovered the longest job title for users in our domain:

Get-ADUser -Filter * -Properties title | Select-Object -ExpandProperty title -Unique | Sort-Object -Property Length | Select-Object -Last 1

I found that our longest job title was 60 characters long (to figure that out, wrap the entire command in parentheses and append ‘.ToCharArray().Count’)

As a side note, I normally never display code samples that use aliases on my blog, but here’s the command that I actually used. This is to illustrate that you don’t need to type out the long-winded version above just to perform a quick task.

get-aduser -filter * -properties title | select -expand title -unique | sort length | select -last 1

Find the MSI ProductCode for Installed Products using the PowerShell App Deployment Toolkit

A quick way to find out MSI product codes for installed products if you have the PowerShell App Deployment Toolkit (PSADT) lying around is to dot source the toolkit’s AppDeployToolkitMain.ps1, and use the Get-InstalledApplication function.

This saves you digging around the registry, and searches both the 64-bit and 32-bit sections of the registry. Also handy, is the fact that the Name parameter on Get-InstalledApplication can take partial names:

image

Server Core: Change NIC binding order with nvspbind

My router VM in my home lab is a Server 2012 R2 set up running RRAS, but with the GUI features removed to save resources on the host.

It has two NICs – one “External”, facing my home router, and the “Internal”, facing the lab VMs. The external NIC just gets a DHCP address, and along with that, my ISP’s DNS server. I’d also set the DNS server on the Internal NIC, but it wasn’t taking precedence due to the binding order of the NICs. I needed to have the router VM query the lab DNS server so that it could join the domain.

Normally, in Server with a GUI, it would be easy to change the NIC binding order. Out of the box without the GUI, not so much. There’s a 3rd-party utility called nvspbind that can be used to make the change.

If you had a NIC named “Internal” that you wanted to bump up the order, and you already had nvspbind somewhere, you could just run the following:

C:\temp\nvspbind\nvspbind.exe /++ Internal ms_tcpip

Here’s my script that I can run on future lab machines to download nvspbind and configure the correct NIC binding order, assuming that there are NICs named “Internal” and “External”:

$nicName = 'Internal'
$downloadUri = 'https://gallery.technet.microsoft.com/Hyper-V-Network-VSP-Bind-cf937850/file/117120/1/Microsoft_Nvspbind_package.EXE'

New-Item -ItemType Directory -Path 'c:\temp' -Force
Invoke-WebRequest -Uri $downloadUri -OutFile 'c:\temp\Microsoft_Nvspbind_package.EXE'
& 'C:\temp\Microsoft_Nvspbind_package.EXE' /T:c:\temp\nvspbind /C /Q
& 'C:\temp\nvspbind\nvspbind.exe' /++ 'Internal' ms_tcpip

Azure: Generate RDP files for multiple VMs

Today I needed to pass on the RDP files for a bunch of Azure VMs to an external developer.

Rather than manually click on the download links in the Azure Portal each time these dev VMs are re-generated, I took the PowerShell route. This was simplified by the fact that all of the VMs in question had a similar Service Name.

Here’s the two lines it took to generate the RDP files, assuming that the Azure Service name for these VMs started with ‘dev-‘, and also assuming that I’m already connected to Azure PowerShell and have the correct subscription selected:

$devVms = Get-AzureVM | Where-Object {$_.ServiceName -like 'dev-*'} 
$devVms | ForEach-Object {Get-AzureRemoteDesktopFile -Name $_.Name -LocalPath "C:\temp\dev_rdp\$($_.Name).rdp" -ServiceName $_.ServiceName}