<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Ron Ben Artzi</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/ronba/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/ronba/feed.xml</link>
    <description>Exchange Escalation Engineer | Japan CSS</description>
    <lastBuildDate>Wed, 17 Apr 2019 03:31:17 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Using PowerShell and the Office 365 REST API with OAuth.</title>
      <link>https://docs.microsoft.com/archive/blogs/ronba/using-powershell-and-the-office-365-rest-api-with-oauth</link>
      <pubDate>Mon, 09 May 2016 16:13:10 GMT</pubDate>
      <dc:creator><![CDATA[ronba]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ronba/?p=37</guid>
      <description><![CDATA[Who is this blog post for:
Administrators who want to use PowerShell to query Office 365 Rest API...]]></description>
      <content:encoded><![CDATA[<h2>Who is this blog post for:</h2>
Administrators who want to use PowerShell to query Office 365 Rest API using OAuth.
<h2>Who isn't this for?</h2>
Anyone who uses any other programming language -
Check out this getting started - it's perfect for you:
<a href="https://dev.office.com/getting-started/office365apis">https://dev.office.com/getting-started/office365apis</a>

<h2>So... Let's get started</h2>
This is also a good time to recommend a really useful tool written by my colleague that allows you to experiment with the OAuth API:
<a href="https://github.com/Microsoft/Office365APIEditor">https://github.com/Microsoft/Office365APIEditor
</a>
I've also used a method to show a nice UI window for the login window I found through:
<a href="https://foxdeploy.com/2015/11/02/using-powershell-and-oauth/">https://foxdeploy.com/2015/11/02/using-powershell-and-oauth/
</a>I recommend reading this - the explanations here are very simple.
I'm basically covering just the implementation in that blog.

Code for the method is directly accessible here:
<a href="https://raw.githubusercontent.com/1RedOne/PSWordPress/master/Private/Show-oAuthWindow.ps1">https://raw.githubusercontent.com/1RedOne/PSWordPress/master/Private/Show-oAuthWindow.ps1</a> but I made a few modifications to it.

Scripting Guy's blog regarding OAuth and Windows Live:
<a href="https://blogs.technet.microsoft.com/heyscriptingguy/2013/07/01/use-powershell-3-0-to-get-more-out-of-windows-live/">https://blogs.technet.microsoft.com/heyscriptingguy/2013/07/01/use-powershell-3-0-to-get-more-out-of-windows-live/</a>
<h2>My motivation for writing this blog:</h2>
I've lately been hearing more and more about the Office 365 REST API from customers who are using it with OAuth and needed to get started with using it.
I also need to do quick verifications and script things quickly and for that PowerShell is just awesome.
PowerShell was missing from the getting-started guide and I've been wanting to try it out for a while.

Step 1:
The first thing we want to is to register a new app:

Sign in with your Office 365 account here.
<a href="https://dev.office.com/app-registration">https://dev.office.com/app-registration</a>


Step 2:
In the App registration page go ahead and register an app.
App name - you can name it whatever you want.
App type - for the purpose of this blog - we can use Web App
Sign on and Redirect URI - for the purposes of this tutorial you can just leave them as is.

Click Register app.
You'll get a client-id and client-secret.
Copy paste everything on this page to a text file, we're going to use it later.

<h2>Time to start scripting!</h2>
Now that we have that, we can get started with the script.

First thing we want to do is define our very handy Show-OAuthWindow method:
<pre>Function Show-OAuthWindow
{
    param(
        [System.Uri]$Url
    )


    Add-Type -AssemblyName System.Windows.Forms
 
    $form = New-Object -TypeName System.Windows.Forms.Form -Property @{Width=440;Height=640}
    $web  = New-Object -TypeName System.Windows.Forms.WebBrowser -Property @{Width=420;Height=600;Url=($url ) }
    $DocComp  = {
        $Global:uri = $web.Url.AbsoluteUri
        if ($Global:Uri -match "error=[^&amp;]*|code=[^&amp;]*") {$form.Close() }
    }
    $web.ScriptErrorsSuppressed = $true
    $web.Add_DocumentCompleted($DocComp)
    $form.Controls.Add($web)
    $form.Add_Shown({$form.Activate()})
    $form.ShowDialog() | Out-Null

    $queryOutput = [System.Web.HttpUtility]::ParseQueryString($web.Url.Query)
    $output = @{}
    foreach($key in $queryOutput.Keys){
        $output["$key"] = $queryOutput[$key]
    }
    
    $output
}
</pre>

What does it do? It just opens the URL we want in a handy little window. Then we use the response to parse a few parameters.

Below this, we can define our variables and the System.Web assembly.
<pre>Add-Type -AssemblyName System.Web
$client_id = "paste the client ID we generated"
$client_secret = "paste the client secret we got"
$redirectUrl = "the redirect URL we used when creating the app."</pre>

We can then create our login URL:
<pre>$loginUrl = "https://login.microsoftonline.com/common/oauth2/authorize?response_type=code&amp;redirect_uri=" + 
            [System.Web.HttpUtility]::UrlEncode($redirectUrl) + 
            "&amp;client_id=$client_id" + 
            "&amp;prompt=login"</pre>

If you're wondering how I knew the format of the request, everything is documented here:
<a href="https://graph.microsoft.io/en-us/docs/authorization/app_authorization">https://graph.microsoft.io/en-us/docs/authorization/app_authorization</a>

Then we'll go ahead and use the method above:
<pre>$queryOutput = Show-OAuthWindow -Url $loginUrl</pre>

We need to enter the username and password we're going to use with this app.
Once we do that, we'll get a page asking us to approve the APP access to the permissions we specified above - so we can go ahead and approve that.

$queryOutput will have the following keys in the hashtable:
code
session_state

The code is what we're interested in - as we're going to go ahead and trade it for an access token.
How are we going to do that?
Well, we're going to make another request:
<pre>
$AuthorizationPostRequest = 
    "grant_type=authorization_code" + "&amp;" +
    "redirect_uri=" + [System.Web.HttpUtility]::UrlEncode($redirectUrl) + "&amp;" +
    "client_id=$client_id" + "&amp;" +
    "client_secret=" + [System.Web.HttpUtility]::UrlEncode("$client_secret") + "&amp;" +
    "code=" + $queryOutput["code"] + "&amp;" +
    "resource=" + [System.Web.HttpUtility]::UrlEncode("https://outlook.office.com/")</pre>

Then we can go ahead and make the request:
<pre>$Authorization = 
    Invoke-RestMethod   -Method Post `
                        -ContentType application/x-www-form-urlencoded `
                        -Uri https://login.microsoftonline.com/common/oauth2/token `
                        -Body $AuthorizationPostRequest</pre>

After execution $Authorization will have our very handy access_token - which we now have to add to every call we make.

Let's see an example:
<pre>$mail = 
    Invoke-RestMethod   -Headers @{Authorization =("Bearer "+ $Authorization.access_token)} `
                        -Uri https://outlook.office.com/api/v2.0/me/messages `
                        -Method Get</pre>
                        
Now we can look at our mail variable:
<pre>$mail.value | select -first 4 | select -ExpandProperty Subject </pre>

That's it!
If you have any questions, let me know :-)
]]></content:encoded>
    </item>
    <item>
      <title>GlobalCatalog not being Recognized as a Global Catalog by Exchange</title>
      <link>https://docs.microsoft.com/archive/blogs/ronba/globalcatalog-not-being-recognized-as-a-global-catalog-by-exchange</link>
      <pubDate>Thu, 24 Mar 2016 14:41:04 GMT</pubDate>
      <dc:creator><![CDATA[ronba]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.technet.microsoft.com/ronba/?p=23</guid>
      <description><![CDATA[A few days ago one of my colleagues had managed to reproduce an issue our customer was seeing and...]]></description>
      <content:encoded><![CDATA[A few days ago one of my colleagues had managed to reproduce an issue our customer was seeing and wanted me to take a look.
We connected to an Exchange lab running Exchange 2013 where my colleague showed me the following:

We ran:
<pre>Get-ExchangeServer -Identity $env:COMPUTERNAME -Status | fl *CURR*,*STAT*

StaticDomainControllers         : {}

StaticGlobalCatalogs            : {SiteXE15DC.contoso.local}

StaticConfigDomainController    : SiteXE15DC.contoso.local

StaticExcludedDomainControllers : {}

CurrentDomainControllers        : {SiteXE15DC.contoso.local, SiteBDC.contoso.local}

CurrentGlobalCatalogs           : {CentralDC.contoso.local}

CurrentConfigDomainController   : SiteXE15DC.contoso.local

</pre>
Can you spot the problem?

That's interesting, the StaticGlobalCatalogs were set to a specific server, but the CurrentGlobalCatalogs is a different server!

First thing I did was to make sure the registry data is correct. A quick check with process monitor showed me the following:
<pre>

15:57:32.7497060 Microsoft.Exchange.Directory.TopologyService.exe 15672 RegQueryValue HKLM\System\CurrentControlSet\Services\MSExchange ADAccess\Profiles\Default\UserGC1\HostName SUCCESS Type: REG_SZ, Length: 42, Data: SiteXE15DC.gtk.local
15:57:32.7497126 Microsoft.Exchange.Directory.TopologyService.exe 15672 RegQueryValue HKLM\System\CurrentControlSet\Services\MSExchange ADAccess\Profiles\Default\UserGC1\HostName SUCCESS Type: REG_SZ, Length: 42, Data: SiteXE15DC.gtk.local
15:57:32.7497192 Microsoft.Exchange.Directory.TopologyService.exe 15672 RegQueryValue HKLM\System\CurrentControlSet\Services\MSExchange ADAccess\Profiles\Default\UserGC1\IsGC SUCCESS Type: REG_DWORD, Length: 4, Data: 1
15:57:32.7497250 Microsoft.Exchange.Directory.TopologyService.exe 15672 RegQueryValue HKLM\System\CurrentControlSet\Services\MSExchange ADAccess\Profiles\Default\UserGC1\IsGC SUCCESS Type: REG_DWORD, Length: 4, Data: 1

</pre>
So, the server is clearly being read correctly from the registry.

&nbsp;

Next I took a look at the 2080 event and saw the following:
<pre>CentralDC.gtk.local CDG 1 7 7 1 0 1 1 7 1,
SiteXE15DC.gtk.local CD- 1 6 7 1 0 1 1 7 1,
SiteBDC.gtk.local CD- 1 6 7 1 0 1 1 7 1
</pre>
The important part here is the CD* notation.
This article gives a pretty good explanation on how to read this.

Roles: The second column shows whether or not the particular server can be used as a configuration domain controller (column value C), a domain controller (column value D), or a global catalog server (column value G) for this particular Exchange server. A letter in this column means that the server can be used for the designated function, and a hyphen (-) means that the server cannot be used for that function. In the example that is described earlier in this article, the Roles column contains the value CDG to show that the service can use the server for all three functions.

That explains the reason the Statically set global catalog wasn't being treated as one, both SiteXE15DC and SiteBDC weren't GlobalCatalogs.

But... I'm sure all 3 of these servers are Global Catalogs! I confirmed that looking at the NTDS data too.

When the MSExchangeADTopologyService starts or when an event 2080 is recorded we reevaluate the ability of servers in our environment to be used for Configuration/DC or GlobalCatalog.

All I had to do is put a breakpoint into the code where we do the TopologyDiscovery and see exactly how this gets evaluated.
The first thing we do to see if a server is evaluated as a GC or not, is look at the NTDS Options.
These are stored at:
CN=NTDS Settings,CN=,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=...

I printed out the value we were getting during loading the servers and found the following:
<pre>
"CentralDC.gtk.local" IsGC
"SiteBDC.gtk.local" 9
"SiteXE15DC.gtk.local" 9
</pre>
9? That means the 1st bit for GC is turned on and then the bit for 8 is turned on.
I thought we might be querying AD in a weird way, so I just queried it with PowerShell:
<pre>
Get-ADObject -LdapFilter "(&amp;(hasMasterNCs=*)(!(msExchCU=*))(|(objectCategory=ntdsDsa)(objectCategory=ntdsDsaRo)))" -Server $env:COMPUTERNAME -SearchBase "CN=Sites,CN=Configuration,DC=gtk,DC=local" -Properties Options | fl Distinguishedname,Name,options


Distinguishedname : CN=NTDS Settings,CN=CENTRALDC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=gt
                    k,DC=local
Name              : NTDS Settings
options           : 1

Distinguishedname : CN=NTDS Settings,CN=SITEBDC,CN=Servers,CN=SiteB,CN=Sites,CN=Configuration,DC=gtk,DC=local
Name              : NTDS Settings
options           : 9

Distinguishedname : CN=NTDS Settings,CN=SITEXE15DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=g
                    tk,DC=local
Name              : NTDS Settings
options           : 9
</pre>
The reason it was set to 9 was because someone ran the following commands:
<pre>
Repadmin /options  +disable_ntdsconn_xlate
Repadmin /add    [/readonly] [/selsecrets]
</pre>
Excellent explanation about this value and when to use it <a href="https://blogs.msdn.microsoft.com/canberrapfe/2012/04/11/granular-active-directory-replication-for-advanced-troubleshooting-scenarios/" title="here">here</a>.

Like explained in the article above, this is not a value which is usually used for a lengthy period of time in a production environment.
We changed this back to 1 on our affected servers and our next event 2080 was back to normal.
<h2>Lessons Learned:</h2>
For a Domain Controller to be recognized as a GlobalCatalog from an Exchange perspective the options property for the NTDS Settings must be set to 1.
Any other value other than 1 will cause the server to not be detected as a GlobalCatalog. 
If you look at what the other Options values do - stop inbound replication, stop outbound replication - you'll probably come to the conclusion that you don't want to use that server as a GlobalCatalog either.

That's it!]]></content:encoded>
    </item>
  </channel>
</rss>