Latest Exchange health check script and deprecated groups

The latests Exchange health check had an interesting addition – identification of deprected exchange groups.

The link that MS provide to get more detail on this doesnt really give any more detail on this…. so i went searching and found https://rafaelpfister.ch/blog/exchange-security-updates-juli-2026  which provides some interesting info – but doesn’t really go into what to check before removing those groups and how to clean up afterwards.

In short, the groups were deprecated around the 2007 timeframe – but subsequent Exchange installs dont actively remove them – so many orgs will still have them there…. and… i get it..

While im a huge fan of keeping things clean and deleting un-used groups, accounts, SIDHistory, foreign security principals etc etc… and AD recycle bin makes this much easier to recover from an accident… its still exchange… so many people err on the side of caution.

 

I think its great that this has been provided in the script – but, it doesnt provide enough information on what the security issue is, what impacts removing the groups could have and how to check… so, thats where i come in i guess… well, until AI scrapes this page and presents my work as its own.

 

Tools i’ll be talking about

Exchange Health check script – https://aka.ms/ExchangeHealthChecker

AD ACL scanner by Canix1 – The best ACL scanner for AD im aware of – https://github.com/canix1/ADACLScanner

Excel – i hope i dont need to explan what excel is

 

The security issue

For many of us running hybrid exchange, we have had Exchange in our forests since Exchange 2000 or there abouts – and have been through many iterations of both AD and Exchange… and generally part due to this, are likely to be running the exchange shared permissions model – and not the split permnissions model introduced in Exchange 2010 SP1 (from memory).

The split permissions model was always interesting to me in that if you moved over to it after being on the shared permissions model, you then had to go back and remove the permissions from the exchange groups – not a task to be taken lightly. If you were setting up a new forest after 2010 SP1 – then it was much easier.

Anyhoo – the guts of it is – that Exchange related groups have permission all through AD in the shared permissions model – and if you haven’t done it already, i suggest you use AD ACL scanner to have a look for yourself.

 

Things to check prior to deleting the AD groups that the healthcheck script recommends

Assuming the health checker script has come back with something similar to the following :

 

Legacy Exchange Security Groups: True

DistinguishedName Scope Members
—————– —– ——-
CN=Exchange Domain Servers,CN=Users,DC=company,DC=com Global 0
CN=Exchange Enterprise Servers,CN=Users,DC=company,DC=com DomainLocal 1
CN=Exchange Recipient Administrators,OU=Microsoft Exchange Security Groups,CN=Users,DC=company,DC=com Universal 1

These legacy Exchange security groups are not used by modern Exchange and carry elevated AD permissions. As a security best practice, review their membership and delete them if they are no longer required. More Information: https://aka.ms/HC-LegacyExchangeGroups

 

You could put your cowboy boots on and just delete them… but… pretending that we are enterprise tech’s – lets actually see where they are in use first.

 

Check 1 – AD

Using ADL ACL Scanner, run a scan with the following options

  • Connect to the top level of your domain (if you have a resource domain or forest root domain, run this on each domain – just in case)
  • Scan depth : subtree
  • Output : csv file
  • Optional – Tick “Inherited permissions” if you havent done this before so you can understand the depth these apply at.

In large enviornments it will take quite a while…. and for most orgs, you would expect the permissions to be allocated at the top level and inherited.

Once you have the SCV, you can filter and search.

I prefer to

  • Use auto filter to filter the inherited column to “False”
  • Filter the “trustee” column to “Exchange”

Now you can see the exchange related permissions in your AD.

As far as what you are looking for – the specific permissions given to the groups that the health checker script identified as deprecated.

In my enviornment i had the following

ObjectClass Trustee Access Inherited Apply To Permission
domainDNS Company\Exchange Enterprise Servers Allow FALSE This Object Only ReadControl
domainDNS Company\Exchange Enterprise Servers Allow FALSE This object and all child objects ListChildren
domainDNS Company\Exchange Enterprise Servers Allow FALSE This object and all child objects Write All Properties displayName
domainDNS Company\Exchange Enterprise Servers Allow FALSE This object and all child objects Write All Properties Exchange Information
domainDNS Company\Exchange Enterprise Servers Allow FALSE This Object Only ExtendedRight Manage Replication Topology
domainDNS Company\Exchange Enterprise Servers Allow FALSE This object and all child objects Write All Properties Public Information
domainDNS Company\Exchange Enterprise Servers Allow FALSE This object and all child objects Write All Properties groupType
domainDNS Company\Exchange Enterprise Servers Allow FALSE This object and all child objects Write All Properties Personal Information
domainDNS Company\Exchange Enterprise Servers Allow FALSE Descendant inetOrgPerson objects Read Permissions,List Contents,Read All Properties,List
domainDNS Company\Exchange Enterprise Servers Allow FALSE Descendant user objects Read Permissions,List Contents,Read All Properties,List
domainDNS Company\Exchange Enterprise Servers Allow FALSE Descendant group objects GenericRead, WriteDacl
organizationalUnit Company\Exchange Enterprise Servers Allow FALSE This object and all child objects ListChildren
organizationalUnit Company\Exchange Enterprise Servers Allow FALSE Descendant group objects GenericRead, WriteDacl
organizationalUnit Company\Exchange Enterprise Servers Allow FALSE This object and all child objects Write All Properties groupType
organizationalUnit Company\Exchange Enterprise Servers Allow FALSE Descendant user objects Read Permissions,List Contents,Read All Properties,List
organizationalUnit Company\Exchange Enterprise Servers Allow FALSE This object and all child objects Write All Properties displayName
organizationalUnit Company\Exchange Enterprise Servers Allow FALSE Descendant inetOrgPerson objects Read Permissions,List Contents,Read All Properties,List
organizationalUnit Company\Exchange Enterprise Servers Allow FALSE This object and all child objects Write All Properties Exchange Information
organizationalUnit Company\Exchange Enterprise Servers Allow FALSE This object and all child objects Write All Properties Public Information
organizationalUnit Company\Exchange Enterprise Servers Allow FALSE This object and all child objects Write All Properties Personal Information

no entries for

Exchange Domain Servers

Exchange Recipient Administrators

 

Check 2 – Exchange

Just because these groups havent been specifically used by Exchange for a long time, doesnt mean that an admin hasnt granted them some type of custom role in Exchange.

To check this you can use

Get-ManagementRoleAssignment | Select-Object Name, Role, RoleAssignee, RoleAssigneeType

an depending on the size of the list, also pipe it out to a csv to be able to search/filter – e.g.

Get-ManagementRoleAssignment | Select-Object Name, Role, RoleAssignee, RoleAssigneeType | Export-Csv C:\Temp\ExchRoles.csv

 

If any of the groups are used here – you can the role update them appropriately.

 

Removing the groups

I initially started writing the various options for removal, but i got rid of that, as i really think there is only one way to go :

 

Empty, then Delete – with AD recycle bin as your fall-back position

Assuming you have AD recycle bin enabled, you can

  • Empty the AD groups specified by the healthcheck script and wait for screams or issues
  • Wait for a time period – say 4 weeks
  • Ensure the on-prem exchange server(s) are rebooted at least ocne during this period
  • Delete the deprecated groups specified by the healthcheck script (either one at a time or all at once)
  • Wait for a time period – say 4 weeks
  • Ensure the on-prem exchange server(s) are rebooted at least ocne during this period
  • Assuming there are no issues, manually delete the now dead SID’s from the AD permissions structure <This is the step that is hard to recover from once done>
  • If there are any issues you can
    • Re-populate the groups
    • or
    • Recover the groups from AD recycle bin for up to 6 months

This approach doesn’t remove the permissions in the directoy – which are very finnicky to add back in should something go wrong – so its the approach i prefer.

 

Experience

Have i done this yet? No… not in prod at least… but i have in test with no issues.

I do expect to do this in prod over the next 2 months.

 

I dont image there many of us hybrid Exchange admins left – or at least those of us that started off doing MSMail to Exchange 4.0 migrations – so feel free to get in contact if you think i have got something wrong or have some clarifications to point out.

Exchange – Can’t remove the domain ‘xy.com’ because it’s referenced in an email address policy by the proxy address template ‘smtp:@xy.com’.

The current company im working for is a large (for Aus) media company and as such, because basically everyone that works here is into marketing and sales, we just have a ridiculous number of domains.

As part of the cleanup process here – i bought up the huge number of domains we had and that maybe some of them weren’t current – and got back confirmation that many weren’t (approx 120)

The first job was removing them from exchange accepted domains, but i kept getting an error

Can’t remove the domain ‘xy.com’ because it’s referenced in an email address policy by the proxy address template ‘smtp:@xy.com’.

Even though the domains were not anywhere in the existing email address policies.

As usual, ADSIEdit to the rescue

navigate to

CN=Recipient Policies,CN=<Your Org Name>,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=xy,DC=com

Find the recipient policy which contains an attribute called “disabledGatewayProxy”

Remove each of the domain values from that attribute

Wait a few minutes for replication

then go back to exchange and remove the accepted domain.

Removing EXO litigation holds and making your mailbox functional again

Recently i had a situation where a user with a shared mailbox which had a ludicrous number of items. He recently started in the role and wanted to “start fresh” – but items could not be deleted.

A quick investigation found that litigation hold had been enabled for the mailbox (in addition to our org wide retention policies). It was unclear why the litigation hold had been enabled – and the reasons were lost due to staff turnover.

Finding and fixing the issue included:

  • get-mailbox <identity> | fl *hold*
    • This command will show the status of all holds on that specific mailbox. In my case, i could see that “LitigationHoldDate” and “DelayHoldApplied” were populated
    • In order to remove these i ran
      • get-mailbox <identity> | Set-Mailbox -LitigationHoldEnabled $false
      • get-mailbox <identity> | Set-Mailbox -RemoveDelayHoldApplied
  • After running these steps – i was still not able to delete items from the mailbox, so i ran
    • get-mailboxFolderStatistics <identity> -FolderScope RecoverableItems | FL Name,FolderAndSubfolderSize,ItemsInFolderAndSubfolders
    • and could see that the recoverable items and purges were both at 100GB – meaning that the quota, which also applied to delete items was full – so i could not yet delete anything more
    • In order to speed up the process of the managed folder assistant doing its job, run
      • Start-ManagedFolderAssistant -Identity <identity> -FullCrawl -HoldCleanup
    • After some time, if you re-run the get-mailboxFolderStatistics command, you should see the recoverable items and purges start to come down
  • Since this mailbox was full and is receiving a very high volume of new mail – the 100GB limit was going to be hit again very quickly – so in order to mitigate that for this initial cleanup, i then set
    • Set-Mailbox -Identity <identity> -RetainDeletedItemsFor 1
    • This will only detain deleted items for 1 day in the dumpster before purging
    • I will set this back to 30 days once the initial cleanup is complete and the mailbox is back to “normal” operation.
  • If the mailbox also has a retention policy in addition to the litigation hold
    • get-mailbox -Identity <identity> -Archive | fl *holds*
      • Take note of the ID of the in place hole
    • Connect-IPPSSession
    • Get-RetentionCompliancePolicy | fl Name,ExchangeObjectId
      • Match up the id of the retention policy to a name
    • Set-RetentionCompliancePolicy -Identity <NameOfPolicy> -AddExchangeLocationException <useridentity>
    • Get-Mailbox <identity> | FL DelayHoldApplied,DelayReleaseHoldApplied
    • Set-Mailbox <identity> -RemoveDelayHoldApplied
    • Set-Mailbox <identity> -RemoveDelayReleaseHoldApplied
    • Start-ManagedFolderAssistant -Identity <identity> -FullCrawl -HoldCleanup
    • <wait>
    • To check the status, use
      • get-mailboxFolderStatistics <identity> -FolderScope RecoverableItems | FL Name,FolderAndSubfolderSize,ItemsInFolderAndSubfolders

I’ve read some articles that say that “Start-ManagedFolderAssistant” needs to be run twice to pick up the new settings – but its all a bit of black magic because we cant see what is happening at the backend…. i tend to run it two or three times – as if those articles are right, im speeding things up, if they are wrong, there is no visible harm.

Issues with mailbox migration, multiple identities where tenant wide retention policies in use

This is a somewhat niche issue – which is why im documententing it here.

Scenario

  • Exchange migration to exchange online which i came into approx 75% of the way through – so i don’t have any history on why some things have happened (and there is no useful doco)
  • Tenant wide retention policies are in place for all data (legislative requirement im led to believe for this client)
  • Identity sync via AADConnect
  • Some mailboxes cannot be moved. Powershell error message from new-moverequest indicates that the identity is not unique

Investigation

  • Start off by looking at the AAD Object sync with
    • Connect-MSOLService
    • (Get-MsolUser -UserPrincipalName identity@goes.here.com).errors.errordetail.objecterrors.errorrecord| fl ErrorCode
    • The output, will likely look something like this:
      • The value “<guid-value>” of property “ArchiveGuid” is used by another recipient object. Please specify a unique value.
  • Next up, we want to have a look at the potential duplicate objects
    • Connect-ExchangeOnline
    • Get-recipient -identity <identity> -includesoftdeletedrecipients
      • This will likely show you 2 (or more) mail user objects
    • To confirm the soft-deleted mailuser object you can use
      • Get-MailUser -resultsize unlimited -SoftDeletedMailUser -Identity <identity> | fl *guid*
      • Notice the ArchiveGUID returned is the same as the ArchiveGUID from the Get-MSOLuser error retrieved earlier in the investigation
    • If you then try and run the obvious next step
      • Get-MailUser -Identity <identity> -SoftDeletedMailUser | Remove-MailUser
      • You will get an error similar to
        • Remove-MailUser: The operation couldn’t be performed because object ‘Soft Delete d Objects\<identity>’ couldn’t be found on ‘SYBPR01A004DC01.AUSPR01A004 .PROD.OUTLOOK.COM’

Now, i know what your thinking “just exclude the mailbox from the retention policy” – and there within lies the issue…. there is no mailbox, only a mail user object, but with an archive mailbox that has been retained by the retention policy after the primary mailbox has been removed. It is then, to my knowledge, impossible to exclude that archive mailbox from retention – as its associated with a mailuser – not a mailbox.

As to how these identities got into this state…. absolutely no idea. I wasn’t around for the earlier parts of the project – but given some other things i’ve seen at the client, standardisation and documentation appear to be frowned upon (which is why i’m getting out ASAP)

 

Solution

The unfortunate solution is to log a call with O365 support.

I included all of the above information in my original support request and was still asked to run a “get-mailbox”… i included all the info again (and again, and again over a teams call showed them the exact same errors and data that i sent them) – and eventually they got the point (took approx 15 business days) and sent it to an internal team, who deleted the objects

Unfortunately i cant post the case number for reference (as it would potentially identify the client) – but maybe pointing MS support to this article might speed the process for others (?). Ideally, there would be a way around this, without engaging support – but there is not as far as I’m aware as of June 2023.

Issue with manually created EXO inbound connector in hybrid environment

Working at a client whom are approx 75% of the way through their migration to exchange online – and there are some odd things im running into – so here’s one of them.

The scenario and issue

  • Exchange hybrid setup, with servers on prem and EXO active. Active mailboxes in both.
  • Mail flow from on prem to EXO shows the following:
    • Outbound SMTP logs shows the message being handed off correctly to EXO
    • Message tracking in EXO shows 3 copies of the message, all of which, when looking into the details are bounces
    • When looking in security.microsoft.com, the messages have been flagged as phishing attempts… with seemingly no way to flag them as not phishing attempts
  • The connectors on-prem looked ok, and after, double, triple and ninieteenth-thousandth checking, they were solid
  • The connectors in EXO were manually created (for reasons i don’t know that pre-date me) and the HCW created connectors had been disabled. No idea why.
  • The connectors in EXO looked fine and validated without any issue
  • After circling around for ages, i compared the disabled HCW connector with the active connect with “get-inboundconnector | fl”
  • This is when i noticed that the HCW created connector had IP’s in the “EFSkipIPs” property

The Fix

  • EFSkipIPs can be configured as per the powershell doco here
  • The EFSkipIPs property looks like it defines IP’s that should be excluded from enhanced filtering. Since the HCW automatically populates this field – most of us will never have to use this…. but if some bright spark decides that the HCW isn’t good enough for them (for whatever reason), then this becomes important.
  • Because i had the previous, disabled connector, created by the HCW – i already knew the IP’s i needed to add.  If you don’t have this, you will need get your the Public IP that is presented to EXO. This could be obtained with something such as www.whatsmyip.com
  • The multi-valued property… well, it would have been nice on the doco page if an example was included… so since there isn’t one in the official doc – here is an example below:

Set-inboundConnector -Identity “OrgToEXO” -EFSkipIPs @{Add=”xx.xx.xx.xx”, “xy.xy.xy.xy”}

  • After that, i needed to wait approx 15 minutes (not sure on the exact time, but it didn’t work straight away) – and bingo-bango – no more mail flow issue

Removing your last exchange on-premise servers after migrating to exchange online

One question that comes up constantly from clients is “Once we move all mailboxes to exchange online, we can remove all our exchange servers, right ?”

 

To which we answer “no – for these reasons….” but its something that has a great deal of trouble sticking…. as the same question gets asked over and over again… sometimes by the same client, sometimes even by the same people.

I stumbled across this today – an article written a few months back which nicely lays out the possible scenario’s

https://docs.microsoft.com/en-us/exchange/decommission-on-premises-exchange

well done to the authors for laying it out plainly!

We don’t really work with any places that will be in scenario 1 anytime soon… but scenario 2 and 3 are spot on… with 3 being the most common scenario for our clients.

O365 ATP recommended config analyser

Came across this after following a post on linked in – checked it out – and its pretty bloody good.

Run the tool – it will spit out a report with recommendations for O365 Advanced threat protection…

The recommendations it spits out are of a pretty good quality

https://www.linkedin.com/pulse/reviewing-your-office-atp-configuration-cam-murray/

its not perfect (these things rarely are) – for example, after setting up DKIM and verifying, i find that it still reports an issue with DKIM – but it isn’t clear what the issue is – but it remains a good checklist enabler.

Exchange 2010 to 2016 mail flow stops with “421 4.4 2 connection dropped due to socket error”

Had a client ring today with a mail flow issue.

They are most of the way through their migration to 2016, but mail flow stopped with the error “421 4.4 2 connection dropped due to socket error” on the exchange 2010 side – when trying to relay to exchange 2016. This occurred after patching – but i’m not 100% on which patches they applied – and didn’t really have the time to find out.

 

Long story short – enabled TLS 1.2 on the Exchange 2010 (on a 2008 R2 OS) as per https://support.quovadisglobal.com/kb/a433/how-to-enable-tls-1_2-on-windows-server-2008-r2.aspx

then restarted the transport service – and mail stated flowing again.

While this is known for Exchange 2019 on Server 2019, where TLS 1.2 is the default – i wasn’t aware this was being retro-fitted……  not a bad thing…. and its only going to catch out the people that are lagging behind… still, considering how many people are lagging behind – this quick post might help!

Exchange migration and AdminSDProp

I recently did a piece of work for a client – moving from Exchange 2010 to 2016. Nothing too exciting…. but they did have an interesting issue.

Once migrating some of test mailboxes, inheritance in AD had to be enabled for a few admin accounts before they could connect via outlook and activesync – to be expected (yes yes, i know admin accounts shouldn’t have mailboxes, but we all know that some clients still do this – and thats not the focus of this post)

What was interesting, was that on further investigation – every account has AdminCount set to “1” and had inheritance disabled – not something to handle manually..

On further investigation, it was found that via some group nesting, all users were members of print operators.

Groups with AdminCount=1 can be located utilising the powershell

Get-ADGroup -LDAPFilter “(admincount=1)”

The client did not want to immediately reverse this due to potential client impacts – and while i disagreed – excluding a group from AdminSDHolder was not something i had looked into before – so i was interested.

A short amount of googling later – and reading a long list of articles, we decided to exclude “print operators” from AdminSDHolder. Two of the better articles (for reference) around this were:

https://social.technet.microsoft.com/wiki/contents/articles/22331.adminsdholder-protected-groups-and-security-descriptor-propagator.aspx

https://social.technet.microsoft.com/Forums/windows/en-US/ddd8d964-6c8b-42b0-b170-2cacaa283d1c/adminsdholder-remove-groups-server-operators-print-operators-backup-operators?forum=winserverDS

The condensed version of the overall solution is:

  • In order to exclude a group from AdminSDHolder, you can utilise ADSIEdit to modify  the property dsHeuristic under “CN=Directory Services,CN=Windows NT,CN=Services,CN=Configuration,DC=YourDomain,DC=com”
  • The value can be calculated depending what groups you wish to exclude, the 2nd linked technet social post above has a really nice explanation
  • in my case, i needed to it to “0000000001000004” (without the quotes)
  • Once this is done, clear the AdminCount property from the appropriate group (in my case, this was “print operators” + another group within the long-line of nesting this client had)
  • Re-run the powershell – “Get-ADGroup -LDAPFilter “(admincount=1)” to verify the groups no longer show up
  • Once this is done, we need to remove the “adminCount” from each of the affected user accounts and enable inheritance – to do that, you can run the below script

 

$users = Get-ADUser -ldapfilter “(objectclass=user)” -searchbase “<DN of path you wish to use for your search base>”
#$users = Get-ADUser -Identity <username> ‘ Use this for testing on a single user first

#Get domain values
$domain = Get-ADDomain
$domainPdc = $domain.PDCEmulator
$domainDn = $domain.DistinguishedName

#HashTable to be used for the reset
$replaceAttributeHashTable = New-Object HashTable
$replaceAttributeHashTable.Add(“AdminCount”,0)

$isProtected = $false ## allows inheritance
$preserveInheritance = $true ## preserve inheritance rules

ForEach($user in $users)
{
# Binding the users to DS
$ou = [ADSI](“LDAP://” + $user)
$sec = $ou.psbase.objectSecurity

Set-ADUser -identity $user -clear adminCount

if ($sec.get_AreAccessRulesProtected())
{
#Changes AdminCount back to &lt;not set&gt;
Get-ADuser $user.DistinguishedName -Properties “admincount” | Set-ADUser -Remove $replaceAttributeHashTable -Server $domainPdc
#Change security and commit
$sec.SetAccessRuleProtection($isProtected, $preserveInheritance)
$ou.psbase.commitchanges()
}
}

 

References:

https://social.technet.microsoft.com/wiki/contents/articles/22331.adminsdholder-protected-groups-and-security-descriptor-propagator.aspx

https://sdbrett.com/BrettsITBlog/2016/12/discover-clear-admincount-powershell/

https://docs.microsoft.com/en-us/previous-versions/technet-magazine/ee361593(v=msdn.10)?redirectedfrom=MSDN

https://blogs.msdn.microsoft.com/muaddib/2013/12/30/how-to-modify-security-inheritance-on-active-directory-objects-using-powershell/

https://blogs.technet.microsoft.com/chadcox/2018/01/08/adposh-find-and-fix-adminsdholder-orphans-admincount/

http://www.selfadsi.org/extended-ad/ad-permissions-adminsdholder.htm

https://social.technet.microsoft.com/Forums/windows/en-US/ddd8d964-6c8b-42b0-b170-2cacaa283d1c/adminsdholder-remove-groups-server-operators-print-operators-backup-operators?forum=winserverDS

 

Active Directory 2019 and Exchange 2019 – what’s new

The short answer is – not much.

Exchange 2019 was released a few weeks back, but was effectively un-usable, as Exchange 2019 requires Windows Server 2019…. and Windows server 2019 got pulled from release (like Windows 10 1809) due to some issues.

Windows Server 2019 was re-released a few days ago, which allowed nerds everywhere (including me) to put Server 2019 and Exchange 2019 into a test environment.

The most striking thing that is immediately noticeable is that everything looks the same…. The install process, the GUI, the management, all looks the same as it did in 2016. To me, this is a good thing – while Microsoft of the past seemed to believe that moving functions between areas was good – some consistency is nice to have too.

 

Active Directory

First appearances indicate there is nothing new in AD 2019, the installation process and management is exactly the same as 2016.

While installing, there is not even an option to set the forest and domain functional level to “2019” – only 2016.

A quick look at the schema version indicates it has increased and quick google finds this article

https://blogs.technet.microsoft.com/389thoughts/2018/08/21/whats-new-in-active-directory-2019-nothing/

So, while there is something new in the schema, its an incredibly small update….. and there are no new features or functionality of any type to focus on.

 

Exchange 2019

Exchange 2019 is a bit the same as AD, everything appears to be the same as Exchange 2016, from the install process to the management interface.

A google comes up with this

https://practical365.com/exchange-server/should-you-upgrade-to-exchange-server-2019/

So there are some changes and feature updates – but these updates may not have an impact/matter to your organization.

 

I found these two releases interesting overall as

  • AD is the core of many enterprise networks
  • Exchange is a core business application

To see a new release of both of these products with very minimal improvements I think demonstrates where all Microsoft’s development effort is going (which, to be fair, we already knew)