Download the Code iconIt seems like "The Cloud" is all we hear about these days, and it's often capitalized as if it were a single monolithic thing. But it's not a single entity. There are many clouds, including the Windows Azure Active Directory (WAAD) cloud and Microsoft Office 365 cloud, both of which offer a vast array of services. You can use the services to augment your on-premises capabilities, or you can migrate to them en masse, without having to go through the hours of project planning and incremental rollout phases that many service additions to a business would require.

Related: Windows Azure Active Directory Developer Preview Now Available

It might surprise you to learn that Microsoft Support engineers have, at best, limited access to the cloud rendition of your environment. This is primarily a result of efforts to make your data private, inaccessible, and inviolate. What this means is that we often have to glean whatever we can using our customers' on-premises context (with their consent) and the automated tools that can run in that context. The best tools that can run in an on-premises context are the Microsoft Online PowerShell tools, which we package together in Support Diagnostics Packages.

I'll go over several common support scenarios that we see and the Windows PowerShell techniques we use in our diagnostics to scope the problem or identify the tenant configuration and ultimately get us closer to problem resolution. I'll first discuss what you need to know when working with tenants and domains. Afterward, I'll discuss what you need to know when working with specific users. This information can be handy if you administer your company's Microsoft Online Services tenant or if you're a Microsoft partner and need to get up to speed quickly on the status of a domain that you're hosting.

Note that all the code snippets you'll see here must be preceded by code to connect with and authenticate to Microsoft Online Services. Here's an example of connection and authentication code:

$Creds = Get-Credential
Import-Module MSOnline
Connect-MsolService -Credential $Creds `
  -WarningAction SilentlyContinue

This code requires that the Windows Azure Active Directory Module for Windows PowerShell be installed, which you can obtain from your online portal administration pages. Note that the back tick (`) in this code and in many of the article's listings means that the executing PowerShell line is continued to the next line. Also note that the code doesn't use aliases. Although aliases are convenient, it's easier to learn from the actual cmdlet names. You can download the article's listings by clicking the Download button.

Getting Useful Tenant and Domain Information

Let's start with getting the top-level domain information. A Microsoft Online Services tenant—no matter whether it's a WAAD or Office 365 tenant—can have multiple domains. Each domain must have a customer-owned public namespace (such as contoso.com). It might also have single sign-on (SSO) integration. SSO means a federated trust, where an on-premises identity solution such as Active Directory Federation Services (ADFS) provides a seamless authentication and authorization experience to resources, no matter whether those resources are in the cloud or in the local enterprise environment.

First, we use the code in Listing 1 to determine whether a particular domain has been verified to prove that the tenant owner owns that namespace. This is important because no other domain configurations (e.g., configuring the domain for SSO or directory synchronization) can occur until the domain is verified.

Listing 1: Code to Verify a Domain
[Array]$Domains = Get-MsolDomain
ForEach ($Domain in $Domains)
  {
  $DomainString = $Domain.Name.ToString()
  if ($Domain.Status -match "Verified")
    {
     "Domain $DomainString is verified."
    }
  if ($Domain.Status -match "Unverified")
    {
    Write-Host "Domain $DomainString is not verified."
    }
}

In Listing 1, we use the Get-MsolDomain cmdlet to create an array and place each domain in the tenant in the array. We then iterate through the domains (which is handy because we don't know how many domains the tenant has) and determine whether that domain's Status property is set to Verified or Unverified. Verified means that the tenant owner has proven that it owns that DNS namespace on the Internet by placing a specific file in its forward lookup zone. Unverified means that the forward lookup zone has never been proven to be one that the tenant owns.

Next, we check to see whether a particular domain is SSO enabled using the code snippet in Listing 2. In this snippet, we again use the Get-MsolDomain cmdlet to build an array of domains. We then iterate through all of them and check whether each domain's authentication type is Managed or Federated. Managed means that there's no federated SSO for the cloud to redirect back to. Federated means that domain is configured to redirect SSO users to the on-premises ADFS server.

Listing 2: Code to See Whether a Domain Is SSO Enabled
[Array]$Domains = Get-MsolDomain
ForEach ($Domain in $Domains)
  {
  $DomainString = $Domain.Name.ToString()
  if ($Domain.Authentication -match "Federated")
    {
    Write-Host "Domain $DomainString is Federated."
    }
  if ($Domain.Authentication -match "Managed")
    {
    Write-Host "Domain $DomainString is Managed."
    }
}

Checking Directory and Password Synchronization

Another common operation that we perform is to see whether WAAD synchronization is being used. Listing 3 shows the code that we use. As you can see, the code uses the Get-MsolUser cmdlet to query all users, then filters the results with the -Synchronized switch. If no users are returned, we know that directory synchronization has never been used.

Listing 3: Code to Check Whether WAAD Synchronization Is Being Used
$AllUsers = Get-MsolUser -All -Synchronized
if ($AllUsers -gt 0)
  {Write-Host "Directory synchronization is enabled."}
  else
{Write-Host "Directory synchronization is not being used `
  or has never synchronized users."}

Next, we determine whether the local directory synchronization server supports password synchronization using the function in Listing 4. This function must be run on the server where WAAD Directory Synchronization (DirSync) is installed.

Listing 4: Function to Check Whether the Local Directory Synchronization Server Supports Password Synchronization
function CheckDirSyncPwdSyncSupport {
  $LegacyDirSyncPath = $env:ProgramFiles + `
    "\Microsoft Online Directory Sync"
  $NewDirSyncPath = $env:ProgramFiles + `
    "\Windows Azure Active Directory Sync"

  # Determine the correct file path for the install.
  if (Test-Path $LegacyDirSyncPath)
    {$DirSyncPath = $LegacyDirSyncPath}
  if (Test-Path $NewDirSyncPath)
    {$DirSyncPath = $NewDirSyncPath}

# Executable path
$DirSyncExePath = $DirSyncPath + `
  "\Microsoft.Online.DirSync.Scheduler.exe"

# Get install version.
# This is a single line that's wrapped for readability onto three lines.
$DirSyncVersion = `
[System.Diagnostics.FileVersionInfo]::`
GetVersionInfo($DirSyncExePath).FileVersion

# Version that supports password sync
$DirSyncPwdVer = "1.0.6385.0012"

  if ($DirSyncVersion -ge $DirSyncPwdVer)
    {return $true}
    else
    {return $false}
  }

$PwdSupport = CheckDirSyncPwdSyncSupport

if ($PwdSupport -eq $true)
  {"The DirSync server supports password synchronization."}
  else
  {"The DirSync server does not support `
    password synchronization."}

The function in Listing 4 first checks to see where the DirSync binaries are installed by doing some path checks with the Test-Path cmdlet. (This step is needed because the paths changed between DirSync versions). The function then constructs the path string to the Microsoft.Online.DirSync.Scheduler.exe file and checks its file version. If the version is greater than or equal to 1.0.6385.0012, we can conclude that the server supports password synchronization.

Getting Handy User-Specific Information

Getting the specifics about a user's configuration can be handy. Here's the information that we commonly look at and how we get it.

We first check to see whether a particular user has synchronized with the Microsoft cloud. If so, we then check to see when the last synchronization occurred. This information is invaluable for Exchange Online mailbox provisioning scenarios. The reason why lies in the fact that the mailbox provision (or some updates) for existing on premises users can't occur until the user has done a directory synchronization from on-premises to the cloud. So, finding out whether the user has ever synced and, if so, when the last one occurred can tell you where the user is in the mailbox provisioning process. Listing 5 shows the code to get this information.

Listing 5: Code to Check Whether a Particular User Has Synchronized with the Microsoft Cloud and When the Last Synchronization Occurred
$UserPrincipalName = Read-Host "Enter the user principal name of the user to check."
$User = Get-MsolUser -UserPrincipalName $UserPrincipalName

if ($User.ImmutableId -eq $null)
  {
  Write-Host "The user $User.UserPrincipalName `
    is not a DirSync user."
  }
  else
  {
  $UserUPNString = $User.UserPrincipalName.ToString()
  $UserDirSyncTimeString = $User.LastDirSyncTime.ToString()
Write-Host "The user $UserUPNString is a DirSync user `
  and last synchronized $UserDirSyncTimeString."
}

Synchronizing Passwords

Password synchronization can seem like a big black box to an administrator. The script in Listing 6 can help clear things up a bit so you don't have to look in several different places to find out whether a password has been synced. This PowerShell script automatically checks all the different locations for the user principal name (UPN) you supply.

Listing 6: Script to Find Out Whether a Password Has Been Synced
function GetPwdLastSet
  { param ($username)
  $searcher=New-Object DirectoryServices.DirectorySearcher
  $searcher.Filter="(&(samaccountname=$username))"
  $results=$searcher.findone()

  $Time = [datetime]::`
    fromfiletime($results.properties.pwdlastset[0])
  return $Time
  }

function GetUserDN
    { param ($username)
  $searcher = New-Object DirectoryServices.DirectorySearcher
  $searcher.Filter = "(&(samaccountname=$username))"
  $results=$searcher.findone()
  $DN = $results.properties.distinguishedname[0]
  return $DN
  }

# Prompt for username to search AD for.
$username = Read-Host "Enter the username of the user `
  to check in samaccountname format."

$OnPremisesPasswordTime = GetPwdLastSet $username
$UserDN = GetUserDN     $username

$PaswordSynced = $False
Get-EventLog "Application" |
  Where-Object {(($_.EventID -eq 657) -and `
  ($_.Message -match $UserDN) -and `
  ($_.TimeGenerated -gt $OnPremisesPasswordTime))} |
  % {
# If event is found, set status to $True since the
# password has synced to the cloud successfully.
$PaswordSynced = $True
$TimeString = $_.TimeGenerated.ToString()
Write-Host "The password for user $username has synced `
  to the cloud since the last password change `
  on-premises. It last synced successfully $TimeString"
}

if ($PaswordSynced -eq $false)
  {Write-Host "The user $username has not synced to the `
    cloud since the last on-premises password change."}

You must run the script on the directory synchronization server, although you could add remote capability to it. This script performs an LDAP query against the on-premises AD to see when the specified user (which you supply at the console) last changed his or her password. The script then searches through the Application event log for:

  • The user's distinguished name (DN), which must be unique according to AD rules
  • The event ID of 657, which is the event that signifies a Success event for the password sync
  • The time index for event 657, which needs to be after the time index for the on-premises password change

This script is run entirely on-premises and requires elevated privileges, such as Domain Administrator for the domain.

Determining Whether a User Is a Tenant Administrator

Determining whether a user is a Tenant Administrator (also known as a Company Administrator) is useful in scenarios in which there's a problem with the on-premises ADFS server. Any Tenant Administrator who is enabled for SSO will be redirected back to that server and, as a consequence, be caught in a catch-22 situation with sign-on failing. So, finding the Tenant Administrator account that isn't also using SSO can help get you out from between a rock and hard place. (If all three situations—the user is a Tenant Administrator, the user is SSO enabled, and the on-premises ADFS server is having problems—are occurring, the user account can't be used. The reason is that the user is redirected to the on-premises ADFS server for sign on, but that server isn't functioning. So, finding a user account that's a Tenant Administrator but not SSO enabled is a way to save your bacon, so to speak.)

You can use the script in Listing 7 to determine whether a user is a Tenant Administrator. This script uses several cmdlets to retrieve information. For example, it first uses the Get-MsolRole cmdlet to get the globally unique identifier (GUID) for the Company Administrators group, then uses that GUID with the Get-MsolRoleMember cmdlet to query the group's membership. After the script has the list of members, it looks through the list for a user that has an email address that matches the UPN that was supplied. This routine will work well for other roles, too. Simply look through the output returned by the Get-MsolRole cmdlet and use the desired role's GUID instead.

Listing 7: Script to Determine Whether a User Is a Tenant Administrator
$username = Read-Host "Enter the username of the user `
  to check in userprincipalname format"
if ($username -notmatch "@")
{$username = Read-Host "Incorrect format for user entered. `
  Enter the username of the user to check `
  in userprincipalname format" }

$UserInfo = Get-MsolUser -UserPrincipalName $username `
  -ErrorAction SilentlyContinue
$MSOLRole = Get-MsolRole

if ($MSOLRole -ne $null)
  {
  $AdminRoleGUID = $MSOLRole[1].ObjectId
  $AdminRoleMembers = `
    Get-MsolRoleMember -RoleObjectId $AdminRoleGUID
  # Compare member lists to see if the
  # current user is a member.
  ForEach ($AdminRoleMember in $AdminroleMembers)
    {
    if ($AdminRoleMember.EmailAddress `
      -eq $UserInfo.UserPrincipalName)
      { $IsTenantAdmin = $True}
    }
  }
if ($IsTenantAdmin -eq $true)
  {Write-Host "The user $username is `
    a tenant administrator."}
else {Write-Host "The user $username is not `
  a tenant administrator."}

Determining Whether a User Is Licensed for Specific Microsoft Online Services

Licensing is a complex area. Getting a user's licensing details displayed in front of you can help you rule out the common problem of users not having a particular service working simply because they aren't licensed for it.

The code in Listing 8 determines whether a user is licensed for specific Microsoft Online Services. It simply looks through the returned licensing information for the user and matches the GUID associated with a specific service license.

Listing 8: Code to Determine Whether a User Is Licensed for Specific Microsoft Online Services
$username = Read-Host "Enter the username of the user `
  to check in userprincipalname format"
if ($username -notmatch "@")
{$username = Read-Host "Incorrect format for user entered. `
  Enter the username of the user to check `
  in userprincipalname format" }
$UserInfo = Get-MsolUser -UserPrincipalName `
  $username -ErrorAction SilentlyContinue

$LicenseDetails = New-Object PSObject
Add-Member -InputObject $LicenseDetails `
  -MemberType noteproperty -Name "User License Details" `
  -Value $username
$Licenses = $UserInfo.Licenses[0].ServiceStatus
ForEach ($License in $Licenses)
  {
  switch ($License.ServicePlan.ServicePlanId)
    {
    'bea4c11e-220a-4e6d-8eb8-8ea15d019f90'
      {Add-Member -InputObject $LicenseDetails `
      -MemberType noteproperty `
      -Name "RMS_S_ENTERPRISE (Rights Management Service)" `
      -Value $License.ProvisioningStatus}
    '43de0ff5-c92c-492b-9116-175376d08c38'
      {Add-Member -InputObject $LicenseDetails `
      -MemberType noteproperty `
      -Name "OFFICESUBSCRIPTION (Office Professional Plus)" `
      -Value $License.ProvisioningStatus}
    '0feaeb32-d00e-4d66-bd5a-43b5b83db82c'
      {Add-Member -InputObject $LicenseDetails `
      -MemberType noteproperty `
      -Name "MCOSTANDARD (Lync Online)" `
      -Value $License.ProvisioningStatus}
    'e95bec33-7c88-4a70-8e19-b10bd9d0c014'
      {Add-Member -InputObject $LicenseDetails `
      -MemberType noteproperty `
      -Name "SHAREPOINTWAC (Microsoft Office Web Apps)" `
      -Value $License.ProvisioningStatus}
    '5dbe027f-2339-4123-9542-606e4d348a72'
      {Add-Member -InputObject $LicenseDetails `
      -MemberType noteproperty `
      -Name "SHAREPOINTENTERPRISE (SharePoint Online)" `
      -Value $License.ProvisioningStatus}
    'efb87545-963c-4e0d-99df-69c6916d9eb0'
      {Add-Member -InputObject $LicenseDetails `
      -MemberType noteproperty `
      -Name "EXCHANGE_S_ENTERPRISE (Exchange Online E3)" `
      -Value $License.ProvisioningStatus}
    '19ec0d23-8335-4cbd-94ac-6050e30712fa'
      {Add-Member -InputObject $LicenseDetails `
      -MemberType noteproperty `
      -Name "EXCHANGE_ENTERPRISE (Exchange Online E2)" `
      -Value $License.ProvisioningStatus}
    '4b9405b0-7788-4568-add1-99614e613b69'
      {Add-Member -InputObject $LicenseDetails `
      -MemberType noteproperty `
      -Name "EXCHANGESTANDARD (Exchange Online E1)" `
      -Value $License.ProvisioningStatus}
    }
  }
$LicenseDetails | FL

The script displays the results, which include human-readable labels for the service licenses, as shown in Figure 1. Note that Success means the user is licensed for a service. Any other result (e.g., PendingInput, Disabled) means that the user isn't licensed.

Getting a User's Licensing Details

A Must-Have Skill

I hope that I have shed some light on the complex administrative world of WAAD and Office 365. In general, knowing how to use PowerShell is a good thing, but when you're administering Microsoft cloud assets, knowing how to use PowerShell is a must-have skill. The PowerShell code snippets I shared will save you time, energy, and frustration. If not, please feel free to call us at Microsoft Support. We're here to help.