Archive

Posts Tagged ‘one-liner’

One-Liner: Get Your Office 365 Tenant ID

November 8th, 2017 No comments

Office 365 logoThere are occasions that you’ll need your Office 365 tenant ID. The tenant ID is just a GUID assigned to your tenant. You can look it up in the Office 365 admin portal by peeking under Admin>Admin Centers>Azure AD>Azure Active Directory>Properties, and you’ll see the tenant ID in the ‘Directory ID’ field. That’s quite a few clicks, AND you have to log in to the Office 365 portal. Over time, there have been other places in the Office 365 portal where you can find it as well. All of them requiring a handful of clicks.

If you’re logged into your tenant via the SkypeOnlineConnector PowerShell module, you can use the following to get your tenant ID:

Get-CsTenant | Select-Object DisplayName, TenantID

Note, if you’re not logged in via the SkypeOnlineConnector, you can run the following first after installing the SkypeOnlineConnector:

Import-Module -Name SkypeOnlineConnector
$session = New-CsOnlineSession -Credential $(Get-Credential)
Import-PSSession -Session $session

Just like the portal method, this requires you to be logged in. There’s a similar method if you’re connected to Microsoft Azure Active Directory Module for Windows PowerShell using Get-MsolPartnerContract, but with the same limitations. You get the idea. But sometimes you just need the tenant ID without having to login to anything. Well, a PowerShell one-liner to the rescue! Just change the ‘mycompany.onmicrosoft.com’ to your Office 365 domain name in the line below and run it in PowerShell:

(Invoke-WebRequest -Uri 'https://login.windows.net/mycompany.onmicrosoft.com/.well-known/openid-configuration' | ConvertFrom-Json).authorization_endpoint.Split('/')[3]

If you look at that one-liner, you see that we’re merely invoking a web request to a specific URI, converting the Json format that it returns, and then grabbing a bit of the resulting Uri.

If you want it to be a little more flexible, we can adjust the code to prompt for a domain name, in case you want to use it in various scripts, as well as suppress the verbose output:

(Invoke-WebRequest -Uri "https://login.windows.net/$(Read-Host -Prompt 'enter domain name')/.well-known/openid-configuration" -Verbose:$false | ConvertFrom-Json).authorization_endpoint.Split('/')[3]

Of course, us consultant types can turn that into a function and toss it into our PowerShell profiles so that it’s always available. This method works with either your default onmicrosoft.com or your primary vanity domain name. I don’t have a tenant with multiple domain names to test with, but I surmise that it works the same.

While I had this post in draft, my buddy Tony Redmond (@12knocksinna) tweeted a link to an article that shows you how to retrieve the tenant ID using a web browser. From a web browser, you can also get the information by going to the same URL as determined above:

https://login.windows.net/mycompany.onmicrosoft.com/.well-known/openid-configuration

In the results, look for a line that begins with ‘authorization_endpoint’ (usually the first line), and you’ll see your tenant ID GUID in the URL on that line.

As you can see, there are multiple ways to get the tenant ID. Some require PowerShell, some don’t. Some require you to login, some don’t.

 

One Liner: Set-TaskbarGrouping – Configure Desktop Taskbar Grouping

February 18th, 2015 No comments

In One Liner: Configuring Shutdown Tracker in Windows Server I mentioned that it’s often preferable to quickly configure some server settings when building servers. As a consultant, I like to set up my server profile when building servers in a manner that’s efficient and convenient for me. One thing that drives me completely insane is the default taskbar group setting. Taskbar grouping is how Windows groups common items together on the taskbar. By default, all similar items are lumped together, i.e. all Internet Explorer windows. So to go back to an IE window could take two mouse clicks instead of one. Let’s take a look at streamlining this configuration for Server 2012 and Server 2012 R2.

Taskbar grouping has three settings. The default “always combine” mentioned previously, “combine when taskbar full” which doesn’t start grouping until there are enough items to fill the taskbar, and my favorite, “never combine”. As you can probably guess, “never combine” doesn’t group taskbar items at all. Since I usually don’t have more than 4 or 5 apps open when building servers, this suits my style.

Just like the shutdown tracker, this setting is stored in the registry. A one liner for this would look like this:

Set-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -Name TaskbarGlomLevel -Value 0

0 is the value for “always combine”, 1 for “combine when taskbar full” and 2 for “never combine”. In order for the setting to take effect, one of two things has to happen. Either log off/restart, or restart the explorer.exe process. The later can be performed by running the following:

Stop-Process -ProcessName explorer -force

If you’d like to use a function for this, we can use something like the code below in our server build script:

function Set-TaskbarGrouping {
    [CmdletBinding(SupportsShouldProcess, SupportsPaging, DefaultParameterSetName = 'NeverCombine')]
    param(
        # Always combines similar shortcuts into groups
        [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'AlwaysCombine')]        
        [switch] $AlwaysCombine,
        
        # Combines similar shortcuts into groups only when the taskbar is full
        [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'CombineWhenTaskbarFull')]
        [switch] $CombineWhenTaskbarFull,
        
        # Never combines similar shortcuts into groups
        [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'NeverCombine')]
        [switch] $NeverCombine,
        
        # Restarts explorer in order for the grouping setting to immediately take effect. If not specified, the change will take effect after the computer is restarted
        [switch] $NoReboot
    )
    switch ($PsCmdlet.ParameterSetName) {
        'AlwaysCombine' {
            Set-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -Name TaskbarGlomLevel -Value 0
        }
        'CombineWhenTaskbarFull' {
            Set-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -Name TaskbarGlomLevel -Value 1
        }
        'NeverCombine' {
            Set-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced' -Name TaskbarGlomLevel -Value 2
        }
    }
    if ($NoReboot){
        Stop-Process -Name explorer -Force
    }else{
        Write-Verbose -Message 'Change will take effect after the computer is restarted'
    }
} # end function Set-TaskbarGrouping

I use parameter set names so that only one of the parameters can be used when the function is called. The three options are “NeverCombine” “CombineWhenTaskbarFull” and “AlwaysCombine”. But since I define the parameters in a param block, you get tab completion. So no need to even remember the options. For example:

Set-TaskbarGrouping -NeverCombine

If you also include the -NoReboot parameter when calling the function, it will restart explorer.exe to avoid the need to log off/restart.

One Liner: Configuring Shutdown Tracker in Windows Server

February 17th, 2015 3 comments

When you spend time building servers, there are often some minor tweaks that you use to make life easier. In many environments, Group Policy Objects (GPOs) are used to configure these settings. But in a lot of environments, that’s not the case. If you build a lot of servers, you may have some scripts to help streamline the process. I often see this being the case among consultants who are engaged to deploy a solution. If you’ve followed my blog for a while, you know that’s what I do. And I look for many ways to streamline the deployment. Many solutions I write are all about the actual deployment, whereas this particular post is about the working environment I’ll be spending time in.

One thing that always drives me nuts is the Shutdown Tracker. That’s the little dialog box that pops up when you want to restart or shutdown a server. You’re presented with a prompt to pick the reason why you’re restarting or shutting down. While this can certainly have its place in an enterprise environment, it’s not generally needed during a deployment. And it’s not likely needed in a lab environment where you might be testing various configurations and restarting often. So let’s gag that annoying prompt.

To disable Shutdown Tracker, open an elevated PowerShell prompt and enter the following one line:

Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Reliability" -Name ShutdownReasonOn -Value 0

This will take care of the problem. If you later want to enable the Shutdown Tracker, you can simply run it again, specifying a 1 for the value.

We can make this a little more flexible by creating a function to let us enable or disable as needed.

function Set-ShutdownTracker {
	[CmdletBinding(SupportsShouldProcess = $True, SupportsPaging = $True, DefaultParameterSetName = "disabled")]
	param(
		# Disable the shutdown tracker
		[Parameter(ValueFromPipeline = $False, ValueFromPipelineByPropertyName = $True, ParameterSetName = "disabled")]
		[switch] $Disabled,
		
		# Enable the shutdown tracker
		[Parameter(ValueFromPipeline = $False, ValueFromPipelineByPropertyName = $True, ParameterSetName = "enabled")]
		[switch] $Enabled
	)
	switch ($PsCmdlet.ParameterSetName) {
		"enabled" {
			Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Reliability" -Name ShutdownReasonOn -Value 1
		}
		"disabled" {
			Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Reliability" -Name ShutdownReasonOn -Value 0
		}
	}
} # end function Set-ShutdownTracker

And the script can be called with either the -Enabled or -Disabled parameters.

Adding the one liners or the function to your deployment scripts might make life a little easier.

One Liner – See Number Of Connected Users, Endpoints On A Lync Front End Server

January 22nd, 2015 5 comments

A question went around an internal DL at work today asking if anyone knew off the top of their head the name of performance counters that show connected users and endpoints. While digging up the answer, I started thinking – this would be a great little one liner.

My esteemed colleague Ron Cook (@roncook925) beat me to supplying the answer to the DL question. The two counters are:

LS:USrv – Endpoint Cache\USrv – Active Registered Endpoints
LS:USrv – Endpoint Cache\USrv – Active Registered Users

Endpoints is always higher than users, in my experience. There are always some users who are connected via mobile devices and rich client, or via OWA, or LPE. So I like to query both.

PowerShell has a great cmdlet called Get-Counter which, as you can guess, can query performance counters. There’s a pretty good tutorial on how to retrieve perfmon counter data for Lync related counters by the Lync PowerShell group at Microsoft in How Do We Love Performance Counters” Let Us Count the Ways. So let’s take a look at how we can get the data we need.

In this case, we’ll query the two counters mentioned above with one line. This is supported in Get-Counter by just separating the counters with a comma. We’ll select an expanded property called CounterSamples, which holds the data we need (among other info). And lastly, we’ll output the path (counter name), and something called the CookedValue, which is the actual counter value contained within CounterSamples. I know, CookedValue sounds like it could be just made up numbers, like those you get from a shifty accountant. But it is truly the value we want.

Plug this into your console as one long line:

Get-Counter "\LS:USrv - Endpoint Cache\USrv - Active Registered Endpoints","\LS:USrv - Endpoint Cache\USrv - Active Registered Users" | Select-Object -ExpandProperty CounterSamples | Format-Table Path,CookedValue -Auto

That will give you a quick point-in-time snapshot of the number of users and endpoints connected to the front end, as shown below.

perfmon

The blurred text is just the front end name. If you’d like to query a remote front end, just tack on the ComputerName parameter, such as:

Get-Counter "\LS:USrv - Endpoint Cache\USrv - Active Registered Endpoints","\LS:USrv - Endpoint Cache\USrv - Active Registered Users" -ComputerName frontend.contoso.com | Select-Object -ExpandProperty CounterSamples | Format-Table Path,CookedValue -Auto

For those wondering why I’m using Format-Table and the -Auto parameter, it’s because the counter path value is so long that it would otherwise get truncated short enough to where you wouldn’t know which counter was tied to which value.

One Liners: Finding Elevated Accounts That Are Enabled For Lync & Skype for Business

November 18th, 2014 No comments

Lync 2013 logo 128x128One thing I see while doing Lync environmental health checks for some customers is some elevated accounts that are enabled for Lync. An example is members of the Domain Admins group. This can be somewhat problematic, especially for administration of those elevated accounts. For security reasons, it is not recommended to enable members of Domain Administrators group for Lync.

You cannot use Lync Server Control Panel to manage users who are members of the Domain Admins Active Directory group. For Domain Admins users, you can use Lync Server Control Panel only to perform read-only search operations. Attempting to perform write operations (such as enable or disable for Lync Server Control Panel, change pool or assigned policies, telephony settings, SIP address) on an elevated user will yield an “Access Denied” error. To perform write operations on a member of Domain Admins, you must use Lync Server Management Shell (PowerShell) cmdlets while logged on as a member of Domain Admins.

For more information please refer to this Microsoft page: User accounts enabled for Lync Server 2013

To query an elevated group, such as Domain Admins, for Lync enabled users, use the following:

(Get-ADGroupMember "Domain Admins").DistinguishedName | Get-CsUser -ErrorAction SilentlyContinue | Format-Table DisplayName,SipAddress

You can replace the “Domain Admins” with the name of any group, really. When you run it, you’ll end up with something like:

PS C:\> (Get-ADGroupMember "Domain Admins").DistinguishedName | Get-CsUser -ErrorAction SilentlyContinue | Format-Table DisplayName,SipAddress

DisplayName                                                 SipAddress
-----------                                                 ----------
Services                                                    sip:services@contoso.com
Dan Giles                                                   sip:dan.giles@contoso.com
Neil Armstrong                                              sip:neil.armstrong@contoso.com
Dawn Lopes                                                  sip:dawn.lopez@contoso.com
Bob Seger                                                   sip:bob.seger@contoso.com
Gail O'Grady                                                sip:gail.ogrady@contoso.com
Troy Dallas                                                 sip:Troy.Dallas@contoso.com
Steve Carrell                                               sip:steve.carrell@contoso.com

You can Lync disable these users for Lync, using the Disable-CsUser cmdlet. This can be done either individually using the -Identity parameter, or everyone at once by pipeline, with something like:

(Get-ADGroupMember "Domain Admins").DistinguishedName | Disable-CsUser -ErrorAction SilentlyContinue

If you have some accounts that were previously members of an elevated group like Domain Admins, but no longer are, then the AdminCount parameter on their account may still be set. This will cause the Access Denied issue to continue. You can manually change this on the user object using ADSIEDIT, or via a script such as Set-AdminUser.

One Liner: See Who You’re Openly Federating With

August 12th, 2013 7 comments

Wanna see who you’re dynamically (openly) federating within Lync Server? It’s pretty straightforward with the Get-EventLog cmdlet. All we need to do is look for the most recent entry in the Lync Server event log with event ID 14601. One an Edge server, open PowerShell and run the following:

Get-EventLog "Lync Server" | Where-Object {$_.EventId -eq 14601} | Select-Object EventId, Message -First 1 | Format-List *

The description of the single returned event will list the domains you’re currently dynamically federating with. You’ll get something back like this:

EventID : 14601
Message : Report of discovered partners that the Access Edge Server is
          currently monitoring.
There are 21 discovered partners, identified by the common name of
their certificate.
Name: sip-na.contoso.com; Domains: contoso.com
Name: sip.fabrikam.com; Domains: fabrikam.com
Name: sip.fourthcoffee.com; Domains: fourthcofee.com
Name: sip.windtiptoys.com; Domains: windtiptoys.com
Name: edge.adatum.com; Domains: adatum.com
Name: sip.humongousinsurance.com; Domains: humongousinsurance.com
Name: LYNC-TOR-Edge.litware.com; Domains: litware.com
Name: sip.northwindtraders.com; Domains: northwindtraders.com
Name: sip.proseware.com; Domains: proseware.com
Name: sip.adventure-works.com; Domains: adventure-works.com
Name: sipeu.alpineskihouse.com; Domains: alpineskihouse.com
Name: Lyncedge.blueyonderairlines.com; Domains: blueyonderairlines.com
Name: ussea-w15access.cohovineyard.com; Domains: cohovineyard.com
Name: sip.cohowinery.com; Domains: cohowinery.com
Name: sip.lucernepublishing.com; Domains: lucernepublishing.com
Name: ap.malvertising.info; Domains: malvertising.info
Name: federation.fineartschool.net; Domains: fineartschool.net
Name: sip.strikestrike.com; Domains: strikestrike.com
Name: lync.treyresearch.net; Domains: treyresearch.net
Name: sip.wideworldimporters.com; Domains: wideworldimporters.com
Name: sip.woodgrovebank.com; Domains: woodgrovebank.com*

A note that as the Message field of the event log entry mentions, these are DISCOVERED PARTNERS – those found through SRV records. If you want to see who you’re federating with using enhanced or direct federation (those specifically defined in your Lync environment), from a non-Edge Lync server, run Get-CsAllowedDomain. For a breakdown on the various types of federation, see Kevin Peter’s excellent post A Few Words on Federation.

One liners: Get All Exchange Users Who Are Configured for Forwarding

May 7th, 2013 4 comments

Exchange 2013 logo 128x128Due to some legal requirements, I had a needed to list all users who were configured in Exchange to forward elsewhere. This was to ensure that mail wasn’t automatically leaving the environment. A simple, single line in the shell is all that’s needed to give me what I need.

Open Exchange Management Shell, and enter this:

Get-Mailbox -Resultsize Unlimited | Where-Object {$_.ForwardingAddress}

We can clean this up and make it a little more presentable using something like:

Get-Mailbox -Resultsize Unlimited | Where-Object {$_.ForwardingAddress} | Select-Object Name, @{Expression={$_.ForwardingAddress};Label="Forwarded to"}, @{Expression={$_.DeliverToMailboxAndForward};Label="Mailbox & Forward"}

And the results are a small table that shows the user name, which object mail is being forwarded to, and whether the mailbox is configured to both store and forward:

forwardedusers

This allowed me to take a look at those user accounts, and disable the forwarding, forcing the users to use their Exchange mailbox.

For a long list, or if you just want the info in a file, we can export the results to a .csv using Export-Csv. To do this, use:

Get-Mailbox -Resultsize Unlimited | Where-Object {$_.ForwardingAddress -ne $null} | Select-Object Name, @{Expression={$_.ForwardingAddress};Label="Forwarded to"}, @{Expression={$_.DeliverToMailboxAndForward};Label="Mailbox & Forward"} | Export-Csv c:\forwardedusers.csv -NoTypeInformation

One liners: List All Users Who Have Send-As Access To Other Mailboxes

October 23rd, 2012 No comments

Exchange 2013 logo 128x128If you need to list all users who have Send-As access to other user’s mailboxes in Exchange, try this little one-liner from Exchange Management Shell:

Get-Mailbox -ResultSize unlimited | Get-ADPermission | Where-Object {$_.ExtendedRights -like "Send-As" -and $_.User -notlike "NT AUTHORITY\SELF" -and (! $_.Deny)} | Format-List Identity,User,AccessRights,IsInherited

This will show you the user who has the right and the mailbox they have rights to.

Send-As rights

Send-As rights. Click to enlarge.

Note that I use fl (Full List) instead of ft (Full Table) because the identity field can be quite long.

One Liners: Setting Recovery Option to ‘restart’ for Lync/Skype4B Services

October 5th, 2011 3 comments

Lync 2013 logo 128x128A client wanted to configure the recovery options for services in Lync to help reduce downtime if/when a service stops. This was no big deal for me, except there are a dozen Lync servers, some of which have quite a few Lync services. I set out to make this easier than manually changing each service’s recovery options.

Service recovery options allow you to define what Windows should do if the service fails. The options are “Take No Action” (the default), “Restart the Service”, “Run a Program”, and “Restart the Computer”. These options can be defined for the first, second, and subsequent service failures. Additional parameters include how long to wait before resetting the failure counter and how long to wait after the service fails before performing the configured failure option. More complex options include running another program:

Default service recovery options before running sc.exe

Default service recovery options before running sc.exe

Unfortunately, PowerShell’s Set-Service doesn’t have a parameter for setting failure options, so we must rely on the command line sc.exe. Sc.exe can be used to create, modify, and delete services. We’ll use this to set our failure options to restart the services. Note: you must use “sc.exe” and not just “sc”, since in PowerShell, “sc” is an alias for Set-Content. The format is

sc.exe [service name] failure reset= [integer] actions= [actions]

Reset is measured in seconds. We’ll use 86400, which is a full 24 hours. Actions are specified as action/wait time in milliseconds. So “restart/5000” means to wait 5000 milliseconds (5 seconds), and then restart the service. The same action will be applied to the first, second, and subsequent service failure.

We’ll use Get-CimInstance with the Win32_Service classname to grab a list of all of the services, piping that to match descriptions that include ‘Lync’ or ‘Skype for Business’, and start modes that are “automatic”. The finished one liner command looks like this:

$services = Get-CimInstance -ClassName 'Win32_Service' | Where-Object {$_.description -imatch 'Lync|Skype for Business' -and $_.StartMode -eq 'Auto'}; foreach ($service in $services){sc.exe failure $service.name reset= 86400 actions= restart/5000}
Recovery options changed

Recovery options changed (click to enlarge)

When we view the properties of the service again, we see that the failure options are set to restart the service, and to reset the counter after 1 day. Since the restart option is only 5 seconds, the “Restart service after” field shows 0 minutes:

Service recovery options after running sc.exe

Service recovery options after running sc.exe

You can also specify different actions for each of the failure instances by adding more actions. For instance, let’s say you want to restart the service for the first and second failures, and reboot the server on subsequent failures. Simply combine the actions together, separating them with a slash, such as:

$services = Get-CimInstance -ClassName 'Win32_Service' | Where-Object {$_.description -imatch 'Lync|Skype for Business' -and $_.StartMode -eq 'Auto'}; foreach ($service in $services){sc.exe failure $service.name reset= 86400 actions= restart/5000/restart/5000/reboot/5000}

It might be wise to not set all of the failure instances to taking action, to prevent the server from getting stuck in a loop of taking action when a service is having serious issues. To only set the first two options, just use a double slash for the third, such as:

$services = Get-CimInstance -ClassName 'Win32_Service' | Where-Object {$_.description -imatch 'Lync|Skype for Business' -and $_.StartMode -eq 'Auto'}; foreach ($service in $services){sc.exe failure $service.name reset= 86400 actions= restart/5000/restart/5000//}

Obviously, a good monitoring solution such as System Center Operations Manager (SCOM) should be used to track and alert when services stop, and when other more serious issues arise. You don’t want to get into a scenario where a service is constantly stopping and being restarted without knowing.

One Liners: Finding Out Which Lync Pool Servers a User is Associated With, and the Preferred Connection Order

August 31st, 2011 2 comments

Lync 2013 logo 128x128Sometimes, you need to do some Lync logging to investigate a problem with a user. If you have multiple servers in a pool, you sometimes have to enable logging on each until you figure out which one the client is actually connecting to. We can find out which servers the user is associated with and the preferred order that the client will connect using the following in the Lync Management Shell:

Get-CsUserPoolInfo

Such as:

Get-CsUserPoolInfo sip:prichard@contoso.com

The output shows us the primary and backup pool FQDNs, and the order in which it will connect to servers in each pool.

PrimaryPoolFqdn                     : lyncpool01.contoso.local
BackupPoolFqdn                      : lyncpool02.contoso.local
UserServicesPoolFqdn                : lyncpool01.contoso.local
PrimaryPoolMachinesInPreferredOrder : {1:2-2, 1:2-1}
BackupPoolMachinesInPreferredOrder  : {1:3-2, 1:3-1}

But what that doesn’t tell us, is the actual names of the servers in the pool, and which one is 1:2-2, and 1:2-1, etc. So we expand a little further and use:

Get-CsUserPoolInfo -Identity "user" | Select-Object -ExpandProperty PrimaryPoolMachinesInPreferredOrder

For example,

Get-CsUserPoolInfo -Identity "prichard" | Select-Object -ExpandProperty PrimaryPoolMachinesInPreferredOrder

This will show the registrar pools and their respective servers in the preferred order the user will connect:

MachineId         : 1:2-2
Cluster           : 1:2
Fqdn              : lyncpoolserver03.contoso.local
PrimaryMacAddress : 000000
Topology          : Microsoft.Rtc.Management.Deploy.Internal.DefaultTopology
MachineId         : 1:2-1
Cluster           : 1:2
Fqdn              : lyncpoolserver02.contoso.local
PrimaryMacAddress : 000000
Topology          : Microsoft.Rtc.Management.Deploy.Internal.DefaultTopology

We see that this user will connect to lyncpoolserver03 first, since it’s listed first. If that server is not available, then the user would be redirected to lyncpoolserver02. Note that this only shows the information for the primary pool. If you have a backup pool, the information for those servers is not shown here (but is shown if you use BackupPoolMachinesInPrefferedOrder as the ExpandedPropery). However, if you do have a backup registrar pool, and want to use it as a backup pool for users homed on the first, you should have Director servers, as mentioned in Another Reason to Include a Director in Your Lync Server 2010 Deployment.

We can then wrap this in a function:

function Get-CsUserConnectionInfo {
 param (
  [parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, Mandatory, HelpMessage = "No username specified")]
  [ValidateNotNullOrEmpty()]
  [string] $user
 )
 Get-CsUserPoolInfo –Identity $user | Select-Object –ExpandProperty PrimaryPoolMachinesInPreferredOrder
} # end function Get-CsUserConnectionInfo

For easy access. Toss it into your PowerShell profile and access it using

Get-CsUserConnectionInfo

Also, the Get-CsConnections.ps1 script will show you the current connections on a per-user basis if needed.