Listing Office 365 Endpoints using PowerShell

An essential part of a successful deployment of Office 365 is to make sure connectivity is optimal and that there are no restrictions being applied for the public endpoints of the service among other factors. this is all detailed on the Office 365 Network Connectivity Principles documentation.

A good overview of the concept was delivered at Ignite 2019 – BRK3000 – Strategies for building effective, optimal and future proof connectivity to Office 365 that will delight your users

One of the tasks is to read the list of the endpoints from the Office 365 URLs and IP address ranges documentation page, due the dynamic nature of the endpoint list a Web Service was made available to ease automation, reporting and 3rd party solutions.

Most of the times, you’ll just want to fetch the list of the URLs, and hand them over to your friendly networking team that would then do their magic. lucky enough PowerShell can be used to get that list easily, here’s an  example:

$endpoints = Invoke-WebRequest "" | ConvertFrom-Json ; $endpoints | ?{$_.serviceArea -eq "Common" -AND $_.Required -eq "True"} | select urls -ExpandProperty Urls

This would request the full list of endpoints from the web service, convert it PowerShell objects from Json and output only the Common Services which are also tagged as Required and list only the Urls.

Another example would be to pull the Optimize category:

$endpoints = Invoke-WebRequest "" | ConvertFrom-Json ; $endpoints | ?{$_.category -eq "Optimize" }


Controlling External Collaboration in Microsoft Teams

Most organizations are required these days to enable collaboration solutions and services with external parties. these may include b2b scenarios or b2c in some cases.

In this blog I’ll be specific to Office 365 Group resources, with Microsoft Teams as an example, you should be aware that each Team in Microsoft Teams corresponds to an Office 365 group.

A common ask is “We wish to enable only named people to join as a guest to our Teams, we don’t want anyone from the organization to invite anyone they like – we need to control this with an internal workflow.”  to satisfy this request, we will use native capabilities within Azure Active Directory and Office 365 to enable group owners to add existing external guests to their team/office 365 group. In addition, we will enable only specific people in the organization with the permission to create and add new guests accounts to the directory.

Note: This is the basic example, this can be adapted to more complex workflows of approval/automation to make this more customized to a specific requirement.

      1. In Office 365 Admin Center, navigate to “Services & add-ins”, select “Office 365 Groups” and enable “Let group owners add people outside the organization to groups”

        Enabling the capability to add external guest accounts to Office 365 groups.
      2. To make sure your guests will also have access to the SharePoint files, enable external sharing using the SharePoint Admin Center. with alignment to our blog theme, we will enable access to Existing accounts only.

        Enabling external content sharing within SharePoint to allow guests to access the files within Microsoft Teams.
      3. We are now ready to limit who can invite external guests. we will configure this using the Azure Active Directory blade in the Azure portal.

        Disabling the capability for non-admin or users with the guest inviter role to add new external guest accounts to the directory

        Disabling “Members can invite” and “Guests can invite” will effectively achieve our goal – “Admins and users in the guest inviter role can invite”

      4. Finally add assign the “Guest inviter” role to whom ever you need

        Assigning the “Guest inviter” role to a specific user account.

The configuration is now complete, a Guest Inviter or an Admin can now add new guests to the directory, and follow whatever internal due diligence or workflow prior to that. Using the (new) AzureAD PowerShell module is my personal recommendation, this way the guest user could be silently added to the directory – and an email will not be sent to him. later on an owner of an Office 365 Group or a Microsoft Teams group could add him easily like any other member – and that will trigger the email invite to the external user.

New-AzureADMSInvitation -InvitedUserDisplayName "John Doe (External)" -InvitedUserEmailAddress "" -SendInvitationMessage:$false -InviteRedirectUrl "http://just.a.placeholder.local"

Using the Azure AD portal is also available to the guest inviter role if PowerShell is out of the question

Adding a new external guest account to the directory.

And if you’ve wondered, this is the error if someone would try to add a new guest account and they don’t have the proper permissions in Microsoft Teams.


Stopping PowerShell from truncating values in tables

A quick PowerShell Tip.

I’ve just stumbled upon this within the scripting guy blog, and I felt I must (re)share it.

Don’t we all hate it when values are displayed and being truncated with “…” ?

PS C:\> Get-Service -Name winmgmt | ft name, DependentServices -AutoSize
Name    DependentServices
----    -----------------
winmgmt {wscsvc, vmms, SUService, SharedAccess...}

It turns out that the system variable $FormatEnumerationLimit is controlling this behavior and there’s a way to properly eliminate these. the article suggests setting to “4” but “-1” will be also a good option.

Enjoy ! ( I know I did )

Azure AD Federated SSO and MFA on-premises with ADFS


2017-07-27 – I’ve included another important note about adding the “Authentication Methods References” claim

Hi again, this is a quick note for anyone who will try to achieve this. I’m writing this post after the topic has been raised from customers and my colleges.

Here are some of the challenges that might brought to you here

  • An Azure AD tenant, with a federated domain pointing to an ADFS
  • ADFS server running 2012 R2 / 2016 with a Multi Factor setup, either with Azure MFA or a 3rd party MFA provider
  • A conditional access / identity protection policy in Azure AD which should enforce Multi Factor authentication
  • ADFS 2016 with Azure MFA set as primary authentication
  • Event ID 364 on the ADFS server – Encountered error during federation passive request. MSIS7042: The same client browser session has made ‘6’ requests in the last ‘4’ seconds

While configuring this, you might get multiple Multi Factor prompts, user performs MFA on-premises, but when redirected back to Azure AD – second factor prompt in cloud is presented. Here’s how you win:

  • Make sure you configure the federated domain setting in Azure AD with -SupportsMFA $true – this will point Multi Factor“requests” to the ADFS:

Set-MsolDomainFederationSettings -DomainName <> -SupportsMFA $true

See more here –

  • In addition to the above you also need to make sure to configure -PromptLoginBehavior Disabled, this will make sure that authentication requests from Azure AD will reach the ADFS “correctly” and won’t cause it to re-authenticate your users:

Set-MsolDomainFederationSettings -DomainName <> -PromptLoginBehavior Disabled

See more here –

Note that for ADFS 2012 R2, the July 2016 update rollup is required for this parameter to work.

With only setting Azure MFA set as Primary, you effectively do NOT perform Multi Factor. please read carefully Configure AD FS 2016 and Azure MFA and see the notes around it.

If you have policy which will enforce Multi Factor and your setup is Azure MFA as Primary – follow the steps above first.

If you’d like to “skip” the second prompt in the cloud, you can either re-think your CA policy 🙂 or follow to add the following claim using a custom rule:

c:[Type == ""]
=> issue(Type = "", Value = "");

This rule will effectively add all your users a static “fake” claim which states they have performed Multi Factor successfully.


GitHub PowerShell Scripts Repo

I’ve setup a GitHub Repo with all of my “public” PowerShell scripts:

The repo holds all of the scripts I’ve blogged about, and some others that I didn’t so make sure to check it out. I intend to keep that repo updated and maintain it with new versions and new scripts.



EMS license assignment to all users made easy

So you’ve purchased Microsoft’s Enterprise Mobility Suite (EMS) licenses, now you need to assign them to users within your organization. A typical situation will be that you already have Office 365 licensed users, and it make sense that all of them will get EMS licenses too.

To achieve this, I would suggest using an Azure AD group with Dynamic Group membership. in this example, the group will include accounts that match ALL these conditions:

  • Enabled users accounts
  • Users with an email address
  • Users with a-non empty Usage Location
  • Synchronized user accounts

Within the Azure AD management portal ( navigate to your Active Directory tenant, and perform the following:

  1. Create a group in Azure AD
  2. Enable it for Dynamic Membership
  3. Enter the advanced rule: (user.accountEnabled -eq “true”) AND (user.mail -ne $null) AND (user.usageLocation -ne $null) AND (user.dirSyncEnabled -eq true)
  4. Assign EMS licenses to the Group

You can read more about Dynamic Group Membership here:

You can also assign licenses with the following methods:

  1. Using the Office 365 Portal – like you would add Office 365 licenses.  This was made available late 2015 –
  2. Using Azure AD PowerShell – ,you can use the following example to assign EMS licenses (with all options) only to users with an Office 365 E3 license:
    $EMSSKU = (Get-MsolAccountSku | ? { $_.AccountSkuID -like "*:EMS"})[0].accountSkuId
    Get-MsolUser -All | ? { $_.licenses.accountsku.SkuPartNumber -eq "ENTERPRISEPACK"} | Set-MsolUserLicense -AddLicenses $EMSSKU
  3. Azure AD Graph API –



High Resolution User Photo Synchronization to Office 365

There are some known limitation and inconsistency with user photos synchronization from Active Directory (using the thumbnailPhoto attribute) to Azure AD and Office 365 apps: Exchange, SharePoint and Skype for Business (aka Lync), specifically if you want to upload high resolution photos of your users that will span across all of Office 365 services.

After spending some research time around this issue, here are my findings:

So to summarize at this point, we want to import high resolution photos to our users. If we rely on the thumbnailPhoto attribute value from Active Directory, we will end up with low resolution images (needs more JPEG effect) or inconsistent results if we look on the SharePoint case.

To upload high resolution photos to Office 365, you should use Set-UserPhoto. This approach works great for Exchange Online, Skype for Business and Azure AD. Although promising, my testing (and others..) showed that if your users’ photos were previously synced to SharePoint Online – they will not necessarily be updated using this method.

Here is my take on solving this, in a somewhat chronological order:

  1. If you need your on-premises thumbnailPhoto attribute populated, keep your current practice of maintaining them.
    1. To avoid future inconsistencies – use “Azure AD app and attribute filtering” to filter out thumbnailPhoto using Azure AD Connect – Custom installation of Azure AD Connect
  2. Utilize the Set-UserPhoto cmdlet in Exchange Online PowerShell to upload your users high resolutions (648×648 px) photos
    1. Note Uploading High Resolution Photos using PowerShell for Office 365 to workaround – “The remote server returned an error: (413) Request Entity Too Large” error if you get this.
  3. To upload your users high resolution photos to SharePoint online use the Core.ProfilePictureUploader sample app from the OfficeDev PnP GitHub repo.
    1. To make this easier to non coders 🙂 I’ve complied the code sample for your usage –
      1. Get the source code here and also make sure to read the FAQ
      2. Follow the explanations in the GitHub page link above around how to run the utility (configuration.xml , the CSV input file and the command syntax).
      3. Make sure your pictures are JPEG files…
    2. This sample app is also documented here, with some additional explanations – Upload user profile pictures sample app for SharePoint

That’s it !

Hope this helps anyone, please comment if it did.


Intune On-Premises Exchange Connector Log

Just a quick note for everyone missing the log files location of Microsoft Intune On-Premises Exchange Connector, seems like there is no documentation on where those files exists. and they are very useful for debugging this component.

This info came from a support case I’ve had with the on-premises connector 🙂


  • Log files are here – C:\ProgramData\Microsoft\Windows Intune Exchange Connector\
  • If you wish to enable verbose tracing for more advanced debugging do the following:
  1. Open the Exchange Connector tracing configuration file. The file is located at: %ProgramData%\Microsoft\Windows Intune Exchange Connector\TracingConfiguration.xml
  2. Locate the TraceSourceLine with the following key: OnPremisesExchangeConnectorService
  3. Change the SourceLevel node value from Warning ActivityTracing (the default) to Verbose ActivityTracing.
      <Key xsi:type="xsd:string">OnPremisesExchangeConnectorService</Key>
      <Value xsi:type="TraceSource">
            <SourceLevel>Verbose ActivityTracing</SourceLevel>
            <FileName>Microsoft\Windows Intune Exchange Connector\Logs\Connector.svclog</FileName>

It is important to note that the ActivityTracing setting should remain or be included with ANY value that is set for the setting.


Lesson learned on PowerShell Modules

Quick note, make sure you do not forget to modify your PSModulePath system variable when installing a new PowerShell module…

Quoting from Installing Modules:

Effect of Incorrect Installation

If the module is not well-formed and its location is not included in the value of the PSModulePath environment variable, basic discovery features of Windows PowerShell, such as the following, do not work.

  • The Module Auto-Loading feature cannot import the module automatically.
  • The ListAvailable parameter of the Get-Module cmdlet cannot find the module.
  • The Import-Module cmdlet cannot find the module. To import the module, you must provide the full path to the root module file or module manifest file.

In my case, I’ve noticed that because I did not modified the PSModulePath system variable, a schedule task of the PowerShell script using that module failed to import the module…. the fun part was that running it in Interactive Mode (while being logged in to the server) actually worked…

Learn from the mistakes of others…


Exchange upgrade fails due to missing language files

Just to help anyone out there that might be facing this issue. I’ve helped troubleshoot an Exchange 2010 RTM upgrade to Exchange 2010 SP3 which kept failing due to missing language files…

Event ID 1603 was also thrown as per to the KB 2784788 – “1635” or “1603” error code when you install update rollups or service packs for Exchange Server 2007 or Exchange Server 2010

The MSILOG indeed showed that the setup was looking for the RTM language files in the original location where the setup files were, but they are long gone… with the RTM DVD no where to be-found (RTM trial files + the oldest Language Pack bundle are in a non compatible version) this situation was doomed to failure.

So, I’ve turned to manually remove any references to the Client / Server language packs on the server, this included removing a whole bunch of registry keys:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v14\Language Packs\ <-- the whole KEY
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Installer\Products\  <-- Whatever "Microsoft Exchange ** Language Pack" I found
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\  <-- Whatever "Microsoft Exchange ** Language Pack" I found
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products  <-- Whatever "Microsoft Exchange ** Language Pack" I found

Following this brutal way, I’ve stumbled upon a way to Applying Small Updates by Reinstalling the Product this actually achieves what the installer wants:

msiexec /i Server<or>ClientLanguagePack.msi REINSTALLMODE=vomus

And it works ! Now, I guess that with a script this would have been much quicker then the registry method, but at least now I’m (and you are) aware of this workaround , and here’s the script for your usage:

** edit the $setuplocation variable for your directory of the servicepack.

$setupLocation = "c:\sp3"
$allDirs = dir $setupLocation -Directory
foreach ($dir in $allDirs)
if (Test-Path ($dir.FullName + "\clientlanguagepack.msi")) {Write-Host "Installing" $ ; Start-Process -FilePath msiexec -ArgumentList /i, ($dir.FullName + "\clientlanguagepack.msi"), "REINSTALLMODE=vomus" -Wait }
if (Test-Path ($dir.FullName + "\serverlanguagepack.msi")) {Write-Host "Installing" $ ; Start-Process -FilePath msiexec -ArgumentList /i, ($dir.FullName + "\serverlanguagepack.msi"), "REINSTALLMODE=vomus" -Wait }


Additional references:

Upgrading Service pack – keep asking for language pack – credit for the REINSTALLMODE=vomus trick

How to restore the missing Windows Installer cache files and resolve problems that occur during a SQL Server update – kb 969052