Skip to main content

Exchange Permission and Reverse Permission Powershell Gui version 1 Exchange 2000/3/7

*** There is an update version that include mailbox acls here http://gsexdev.blogspot.com/2008/10/exchange-reverse-permission-audit.html

Exchange mailbox permissions getting you down, don’t know who has access to who’s mailbox, rouge admins or legacy permissions or configurations keeping you up at night, can’t get any more the 500 horses out of the V8 under the bonnet. Okay seriously this is kind of rollup script of a lot of vbs scripts that I’ve posted over the past couple of years in regards to Exchange Mailbox, Send and Receive As, Store and delegated Admin rights. It enumerates permissions from Active Directory firstly from all Mailbox rights, Send and Receive As Rights for Exchange Users and then all Mailbox Store ACL’s and finally from the Root Exchange container in the configuration partition to work out the Rights that have been delegated for Exchange System Manager Delegation wizard. It adds all the permissions to ADO.NET datatables and does some calculations to work out first how many Implicit ACL’s have been added to exchange Mailboxes and how many users have been given send and receive as right to each mailbox. Then via the magic of Hashtables it does a reverse check for both Mailbox and Send as and Receive As rights so you can tell how many mailboxes a certain user account has been granted access to or has send or receive as rights. It then checks store rights so you can see which accounts have been give super user rights on each of your Stores or if your using Exchange 2007 you can see which accounts have been given impersonation rights on each of your stores. The finally check is the Delegated Admin rights check now this was a port of a VBS script I had for Exchange 2000/3 and while some of the permission maps the same in Exchange 2007 its doesn’t map fully the delegated rights in Exchange 2007 hope to fix this in the next version. To get the Mailbox ACL it uses the msexchmailboxsecuritydescriptor AD property which I posted a more simple demonstration type script last week for doing this in Powershell.

After the script has enumerated all the permissions it then builds a Winform and presents a little gui that shows all the totals in a datagrid. To see the individual ACL’s on a particular mailbox you need to select it (then select the type of ACL’s you want to see) and hit the show ACE’s button. The Permission Type box lets you choose between Mailbox, Store or Delegation objects.

This is version 1 hopefully when I have more time I’d like to add a snapshot function in the script so that it saves the permissions everytime it runs. Then whenever you run the script you’ll have the added functionality of being able to compare what permission changes that have been made between the different time points when you have run the script in the past. This turns it into to a pretty powerful little ongoing auditing tool. Also I’d like to add the ability to export to a csv file (I know more blog promises)

Because the script just uses Active directory via ADSI it should work in any Exchange 2000, 2003 or 2007 network from any workstation that has powershell installed.

I’ve put a downloadable version of this script here the script itself looks like

(Warning this script may cause global warming if you really are getting more then 500 horses out of the V8 under the bonnet that and the fact you leave my poor little 4 cylinder Honda in the dust)

[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")

function enumMailboxperms() {
$root = [ADSI]'LDAP://RootDSE'
$dfDefaultRootPath = "LDAP://" + $root.DefaultNamingContext.tostring()
$dfRoot = [ADSI]$dfDefaultRootPath
$gfGALQueryFilter = "(&(&(&(mailnickname=*)(objectCategory=person)(objectClass=user))))"
$dfsearcher = new-object System.DirectoryServices.DirectorySearcher($dfRoot)
$dfsearcher.Filter = $gfGALQueryFilter
$dfsearcher.PropertiesToLoad.Add("msExchMailboxSecurityDescriptor")
$srSearchResult = $dfsearcher.FindAll()
foreach ($emResult in $srSearchResult) {
$uoUserobject = New-Object System.DirectoryServices.directoryentry
$uoUserobject = $emResult.GetDirectoryEntry()
$emProps = $emResult.Properties
[byte[]]$DaclByte = $emProps["msexchmailboxsecuritydescriptor"][0]
$adDACL = new-object System.DirectoryServices.ActiveDirectorySecurity
$adDACL.SetSecurityDescriptorBinaryForm($DaclByte)
$mbRightsacls =$adDACL.GetAccessRules($true, $false, [System.Security.Principal.SecurityIdentifier])
"Processing Mailbox - " + $uoUserobject.DisplayName
$mbCount = 0
foreach ($ace in $mbRightsacls){
if($ace.IdentityReference.Value -ne "S-1-5-10" -band $ace.IdentityReference.Value
-ne "S-1-5-18" -band $ace.IsInherited -ne $true){
$sidbind = "LDAP://<SID=" + $ace.IdentityReference.Value + ">"
$AceName = $ace.IdentityReference.Value
$aceuser = [ADSI]$sidbind
if ($aceuser.name -ne $null){
$AceName = $aceuser.samaccountname.ToString()
}
if ($rvMailboxPerms.Containskey($AceName)){
$rvMailboxPerms[$AceName] = [int]$rvMailboxPerms[$AceName] +1
}
else {
$rvMailboxPerms.add($AceName,1)
}
If ($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::CreateChild){

[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),"MailboxRight",$aceName,"Full
Mailbox Access",$ace.AccessControlType)
$mbCount++}
If ($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::WriteOwner
-ne 0){
[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),"MailboxRight",$aceName,"Take
Ownership",$ace.AccessControlType)
$mbCount++}
If ($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::WriteDacl){

[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),"MailboxRight",$aceName,"Modify
User Attributes",$ace.AccessControlType)
$mbCount++}
If ($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::ListChildren){

[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),"MailboxRight",$aceName,"Is
mailbox primary owner of this object",$ace.AccessControlType)
$mbCount++}
If ($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::Delete){

[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),"MailboxRight",$aceName,"Delete
mailbox storage",$ace.AccessControlType)
$mbCount++}
If ($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::ReadControl){

[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),"MailboxRight",$aceName,"Read
permissions",$ace.AccessControlType)
$mbCount++}

}
}
$srCount = 0
$Sendasacls = $uoUserobject.psbase.get_objectSecurity().getAccessRules($true,
$false, [System.Security.Principal.SecurityIdentifier])|? {$_.ObjectType -eq
'ab721a54-1e2f-11d0-9819-00aa0040529b'}
$Recieveasacls = $uoUserobject.psbase.get_objectSecurity().getAccessRules($true,
$false, [System.Security.Principal.SecurityIdentifier])|? {$_.ObjectType -eq
'ab721a56-1e2f-11d0-9819-00aa0040529b'}
if ($Sendasacls -ne $null){
foreach ($ace in $Sendasacls)
{
if($ace.IdentityReference.Value -ne "S-1-5-10" -band $ace.IdentityReference.Value
-ne "S-1-5-18" -band $ace.IsInherited -ne $true){
$srCount++
$sidbind = "LDAP://<SID=" + $ace.IdentityReference.Value + ">"
$AceName = $ace.IdentityReference.Value
$aceuser = [ADSI]$sidbind
if ($aceuser.name -ne $null){
$AceName = $aceuser.samaccountname.ToString()
}
[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),"SendAS-RecieveAS",$AceName,"Send
As",$ace.AccessControlType)
if ($rvSendRecieve.Containskey($AceName)){
$rvSendRecieve[$AceName] = [int]$rvSendRecieve[$AceName] +1
}
else {
$rvSendRecieve.add($AceName,1)
}
}

}
}
if ($Recieveasacls -ne $null){
foreach ($ace in $Recieveasacls)
{
if($ace.IdentityReference.Value -ne "S-1-5-10" -band $ace.IdentityReference.Value
-ne "S-1-5-18" -band $ace.IsInherited -ne $true){
$srCount++
$sidbind = "LDAP://<SID=" + $ace.IdentityReference.Value + ">"
$AceName = $ace.IdentityReference.Value
$aceuser = [ADSI]$sidbind
if ($aceuser.name -ne $null){
$AceName = $aceuser.samaccountname.ToString()
}
[VOID]$rsTable.rows.add($uoUserobject.samaccountname.ToString(),"SendAS-RecieveAS",$AceName,"Recieve
As",$ace.AccessControlType)
if ($rvSendRecieve.Containskey($AceName)){
$rvSendRecieve[$AceName] = [int]$rvSendRecieve[$AceName] +1
}
else {
$rvSendRecieve.add($AceName,1)
}
}
}
}
$nmMailboxPerms.Add($uoUserobject.samaccountname.ToString(),$mbCount)
$nmSendRecieve.Add($uoUserobject.samaccountname.ToString(),$srCount)
}
foreach($key in $nmMailboxPerms.keys){
$rvSRRights = 0
$rvMbRights = 0
if ($rvMailboxPerms.Containskey($key)){
$rvMbRights = $rvMailboxPerms[$key]
}
if ($rvSendRecieve.Containskey($key)){
$rvSRRights = $rvSendRecieve[$key]
}

$rs1Table.Rows.Add($key, $nmMailboxPerms[$key],$nmSendRecieve[$key],$rvMbRights,$rvSRRights)

}
# write-host $nmSendRecieve
$dgDataGrid.datasource = $rs1Table
}

function showACL{
if ($ObjTypeDrop.SelectedItem -eq $null -bor $ObjTypeDrop.SelectedItem -eq
"Mailbox"){

if ($AceTypeDrop.SelectedItem -ne $null){
switch($AceTypeDrop.SelectedItem.ToString()){
"Mailbox-Rights" {$rows = $rsTable.Select("MailboxName = '" +
$rs1Table.DefaultView[$dgDataGrid.CurrentCell.RowIndex][0] + "' And ACLType = 'MailboxRight'")}
"SendAs/ReciveAS-Rights" {$rows = $rsTable.Select("MailboxName = '" +
$rs1Table.DefaultView[$dgDataGrid.CurrentCell.RowIndex][0] + "' And ACLType = 'SendAS-RecieveAS'")}
"Reverse-Mailbox-Rights" {$rows = $rsTable.Select("UserName = '" +
$rs1Table.DefaultView[$dgDataGrid.CurrentCell.RowIndex][0] + "' And ACLType = 'MailboxRight'")}
"Reverse-SendAs/ReciveAS-Rights" {$rows = $rsTable.Select("UserName = '" +
$rs1Table.DefaultView[$dgDataGrid.CurrentCell.RowIndex][0] + "' And ACLType = 'SendAS-RecieveAS'")}
default {$rows = $rsTable.Select("UserName = '" +
$rs1Table.DefaultView[$dgDataGrid.CurrentCell.RowIndex][0] + "' Or MailboxName =
'" + $rs1Table.DefaultView[$dgDataGrid.CurrentCell.RowIndex][0] + "'")}
}
}
else{
$rows = $rsTable.Select("UserName = '" +
$rs1Table.DefaultView[$dgDataGrid.CurrentCell.RowIndex][0] + "' Or MailboxName =
'" + $rs1Table.DefaultView[$dgDataGrid.CurrentCell.RowIndex][0] + "'")
}


$frTable.clear()
foreach ($row in $rows){
$frTable.rows.add($row[0].ToString(),$row[1].ToString(),$row[2].ToString(),$row[3].ToString(),$row[4].ToString())
}
$dgDataGrid1.datasource = $frTable
}
else{
$rows = $msrTable.Select("DistinguishedName='" +
$msr1Table.DefaultView[$dgDataGrid.CurrentCell.RowIndex][3] + "'")
$fr1Table.clear()
foreach ($row in $rows){
$fr1Table.rows.add($row[0].ToString(),$row[1].ToString(),$row[2].ToString(),$row[3].ToString(),$row[4].ToString())
}
$dgDataGrid1.datasource = $fr1Table
}
}

function EnumMailStorePerms(){

$dse = [adsi]("LDAP://Rootdse")
$ERtbl = @{}
$ext = [adsi]("LDAP://cn=Extended-rights,"+$dse.configurationNamingContext)
$ext.psbase.children |% {
if ($ERtbl.containskey($_.rightsGuid.ToString()) -eq $false){
$ERtbl.Add($_.rightsGuid.ToString(),$_.displayName.toString())
}
}

$root = [ADSI]'LDAP://RootDSE'
$cfConfigRootPath = "LDAP://" + $root.configurationNamingContext.tostring()
$cfRoot = [ADSI]$cfConfigRootPath
$sQueryFilter = "(objectCategory=msExchPrivateMDB)"
$dfsearcher = new-object System.DirectoryServices.DirectorySearcher($cfRoot)
$dfsearcher.Filter = $sQueryFilter
$srSearchResult = $dfsearcher.FindAll()
foreach ($emResult in $srSearchResult) {
$soStoreobject = New-Object System.DirectoryServices.directoryentry
$soStoreobject = $emResult.GetDirectoryEntry()
$Storeacls = $soStoreobject.psbase.get_objectSecurity().getAccessRules($true,
$false, [System.Security.Principal.SecurityIdentifier])
foreach($ace in $Storeacls){
if ($ace.IdentityReference.Value -ne "S-1-5-7" -band $ace.IdentityReference.Value
-ne "S-1-1-0" -band $ace.IsInherited -ne $true){
$sidbind = "LDAP://<SID=" + $ace.IdentityReference.Value + ">"
$AceName = $ace.IdentityReference.Value
$aceuser = [ADSI]$sidbind
if ($aceuser.name -ne $null){
$AceName = $aceuser.samaccountname.ToString()
}
if ($ERtbl.containskey($ace.ObjectType.ToString())){
$rights = $ERtbl[$ace.ObjectType.ToString()] + " " + $ace.ObjectType.ToString()
}
else {
$rights = $ace.activeDirectoryRights.toString()
}
$msrTable.rows.add($soStoreobject.Name.ToString(),$soStoreobject.DistinguishedName.ToString(),$AceName,$rights,$ace.AccessControlType)

}
}
$soServer = [ADSI]("LDAP://" + $soStoreobject.msExchOwningServer)
"Processing MailStore - " + $soServer
$sgStorageGroup = $soStoreobject.psbase.Parent
$msr1Table.rows.add($soServer.Name.ToString(),$sgStorageGroup.Name.ToString(),$soStoreobject.Name.ToString(),$soStoreobject.DistinguishedName.ToString())
}
}

function enDelExchangeRight(){

$root = [ADSI]'LDAP://RootDSE'
$exRootPath = "LDAP://CN=Microsoft Exchange,CN=Services," + $root.configurationNamingContext.tostring()
$exRoot = [ADSI]$exRootPath
$Storeacls = $exRoot.psbase.get_objectSecurity().getAccessRules($true, $false, [System.Security.Principal.SecurityIdentifier])
foreach($ace in $Storeacls){
if ($ace.IdentityReference.Value -ne "S-1-5-7" -band $ace.IdentityReference.Value
-ne "S-1-1-0" -band $ace.IsInherited -ne $true){
$sidbind = "LDAP://<SID=" + $ace.IdentityReference.Value + ">"
$AceName = $ace.IdentityReference.Value
$aceuser = [ADSI]$sidbind
if ($aceuser.name -ne $null){
$AceName = $aceuser.samaccountname.ToString()
}
switch ($ace.activeDirectoryRights.GetHashCode()){
983551 {$drTable1.rows.add($AceName,"Exchange Full Administration")}
197119 {$drTable1.rows.add($AceName,"Exchange Administration")}
131220 {$drTable1.rows.add($AceName,"Exchange View Only Administrator")}
}


}
}

}


$nmMailboxPerms = @{ }
$nmSendRecieve = @{ }
$rvMailboxPerms = @{ }
$rvSendRecieve = @{ }

$Dataset = New-Object System.Data.DataSet
$rsTable = New-Object System.Data.DataTable
$rsTable.TableName = "Mailbox Rights"
$rsTable.Columns.Add("MailboxName")
$rsTable.Columns.Add("ACLType")
$rsTable.Columns.Add("UserName")
$rsTable.Columns.Add("Rights")
$rsTable.Columns.Add("Status")
$Dataset.tables.add($rsTable)

$Dataveiw = New-Object System.Data.DataView($rsTable1)
$rs1Table = New-Object System.Data.DataTable
$rs1Table.TableName = "ACL-Numbers"
$rs1Table.Columns.Add("AccountName")
$rs1Table.Columns.Add("Mailbox")
$rs1Table.Columns.Add("Send/RecieveAS")
$rs1Table.Columns.Add("Revese_Mailbox")
$rs1Table.Columns.Add("Revese_Send/RecieveAS")
$Dataset.tables.add($rs1Table)

$Dataveiw = New-Object System.Data.DataView($drTable1)
$drTable1 = New-Object System.Data.DataTable
$drTable1.Columns.Add("AccountName")
$drTable1.Columns.Add("DelegatedRights")
$Dataset.tables.add($drTable1)

$frTable = New-Object System.Data.DataTable
$frTable.TableName = "Filtered Mailbox Rights"
$frTable.Columns.Add("MailboxName")
$frTable.Columns.Add("ACLType")
$frTable.Columns.Add("UserName")
$frTable.Columns.Add("Rights")
$frTable.Columns.Add("Status")
$Dataset.tables.add($frTable)


$fr1Table = New-Object System.Data.DataTable
$fr1Table.TableName = "Mailbox Store Rights"
$fr1Table.Columns.Add("MailStore")
$fr1Table.Columns.Add("DistinguishedName")
$fr1Table.Columns.Add("UserName")
$fr1Table.Columns.Add("Rights")
$fr1Table.Columns.Add("Status")
$Dataset.tables.add($fr1Table)

$msrTable = New-Object System.Data.DataTable
$msrTable.TableName = "Filtered Mailbox Store Rights"
$msrTable.Columns.Add("MailStore")
$msrTable.Columns.Add("DistinguishedName")
$msrTable.Columns.Add("UserName")
$msrTable.Columns.Add("Rights")
$msrTable.Columns.Add("Status")
$Dataset.tables.add($msrTable)

$msr1Table = New-Object System.Data.DataTable
$msr1Table.TableName = "Mailbox Store Table"

$msr1Table.Columns.Add("ServerName")
$msr1Table.Columns.Add("StorageGroupName")
$msr1Table.Columns.Add("MailStoreName")
$msr1Table.Columns.Add("DistinguishedName")
$Dataset.tables.add($msr1Table)

$form = new-object System.Windows.Forms.form
$form.Text = "Exchange Permissions Gui"


# Add Show ACE Button

$shaces = new-object System.Windows.Forms.Button
$shaces.Location = new-object System.Drawing.Size(560,19)
$shaces.Size = new-object System.Drawing.Size(90,23)
$shaces.Text = "Show ACE's"
$shaces.Add_Click({showACL})
$form.Controls.Add($shaces)

# Add Object Type Drop Down
$ObjTypeDrop = new-object System.Windows.Forms.ComboBox
$ObjTypeDrop.Location = new-object System.Drawing.Size(160,20)
$ObjTypeDrop.Size = new-object System.Drawing.Size(200,30)
$ObjTypeDrop.Items.Add("Mailbox")
$ObjTypeDrop.Items.Add("Mailbox-Store")
$ObjTypeDrop.Items.Add("Delegated Exchange Admin")
$ObjTypeDrop.Add_SelectedValueChanged({
switch($ObjTypeDrop.SelectedItem.ToString()){
"Mailbox" {$dgDataGrid.datasource = $rs1Table}
"Mailbox-Store" {$dgDataGrid.datasource = $msr1Table}
"Delegated Exchange Admin" {$dgDataGrid.datasource = $drTable1}
}
})
$form.Controls.Add($ObjTypeDrop)

# Add Object Type DropLable
$ObjTypelableBox = new-object System.Windows.Forms.Label
$ObjTypelableBox.Location = new-object System.Drawing.Size(20,20)
$ObjTypelableBox.size = new-object System.Drawing.Size(150,20)
$ObjTypelableBox.Text = "Select Permission Type"
$form.Controls.Add($ObjTypelableBox)

# Add Ace Type DropLable
$AceTypelableBox = new-object System.Windows.Forms.Label
$AceTypelableBox.Location = new-object System.Drawing.Size(660,20)
$AceTypelableBox.size = new-object System.Drawing.Size(80,20)
$AceTypelableBox.Text = "ACE Type"
$form.Controls.Add($AceTypelableBox)

# Add Ace Type Drop Down
$AceTypeDrop = new-object System.Windows.Forms.ComboBox
$AceTypeDrop.Location = new-object System.Drawing.Size(740,20)
$AceTypeDrop.Size = new-object System.Drawing.Size(200,30)
$AceTypeDrop.Items.Add("Mailbox-Rights")
$AceTypeDrop.Items.Add("SendAs/ReciveAS-Rights")
$AceTypeDrop.Items.Add("Reverse-Mailbox-Rights")
$AceTypeDrop.Items.Add("Reverse-SendAs/ReciveAS-Rights")
$form.Controls.Add($AceTypeDrop)

# Select Target Group Box

$OfGbox1 = new-object System.Windows.Forms.GroupBox
$OfGbox1.Location = new-object System.Drawing.Size(12,0)
$OfGbox1.Size = new-object System.Drawing.Size(520,75)
$OfGbox1.Text = "Select Permission Object Type"
$form.Controls.Add($OfGbox1)

# DACL Content Group Box

$OfGbox = new-object System.Windows.Forms.GroupBox
$OfGbox.Location = new-object System.Drawing.Size(550,0)
$OfGbox.Size = new-object System.Drawing.Size(450,75)
$OfGbox.Text = "Show DACL Contents"
$form.Controls.Add($OfGbox)

# Add DataGrid View

$dgDataGrid = new-object System.windows.forms.DataGridView
$dgDataGrid.Location = new-object System.Drawing.Size(10,80)
$dgDataGrid.size = new-object System.Drawing.Size(520,500)
$dgDataGrid.AutoSizeRowsMode = "AllHeaders"
$form.Controls.Add($dgDataGrid)

$dgDataGrid1 = new-object System.windows.forms.DataGridView
$dgDataGrid1.Location = new-object System.Drawing.Size(550,80)
$dgDataGrid1.size = new-object System.Drawing.Size(450,500)
$dgDataGrid1.AutoSizeRowsMode = "AllHeaders"
$form.Controls.Add($dgDataGrid1)


enumMailboxperms
EnumMailStorePerms
enDelExchangeRight

$form.topmost = $true
$form.Add_Shown({$form.Activate()})
$form.ShowDialog()

Popular posts from this blog

The MailboxConcurrency limit and using Batching in the Microsoft Graph API

If your getting an error such as Application is over its MailboxConcurrency limit while using the Microsoft Graph API this post may help you understand why. Background   The Mailbox  concurrency limit when your using the Graph API is 4 as per https://docs.microsoft.com/en-us/graph/throttling#outlook-service-limits . This is evaluated for each app ID and mailbox combination so this means you can have different apps running under the same credentials and the poor behavior of one won't cause the other to be throttled. If you compared that to EWS you could have up to 27 concurrent connections but they are shared across all apps on a first come first served basis. Batching Batching in the Graph API is a way of combining multiple requests into a single HTTP request. Batching in the Exchange Mail API's EWS and MAPI has been around for a long time and its common, for email Apps to process large numbers of smaller items for a variety of reasons.  Batching in the Graph is limited to a m

Sending a Message in Exchange Online via REST from an Arduino MKR1000

This is part 2 of my MKR1000 article, in this previous post  I looked at sending a Message via EWS using Basic Authentication.  In this Post I'll look at using the new Outlook REST API  which requires using OAuth authentication to get an Access Token. The prerequisites for this sketch are the same as in the other post with the addition of the ArduinoJson library  https://github.com/bblanchon/ArduinoJson  which is used to parse the Authentication Results to extract the Access Token. Also the SSL certificates for the login.windows.net  and outlook.office365.com need to be uploaded to the devices using the wifi101 Firmware updater. To use Token Authentication you need to register an Application in Azure https://msdn.microsoft.com/en-us/office/office365/howto/add-common-consent-manually  with the Mail.Send permission. The application should be a Native Client app that use the Out of Band Callback urn:ietf:wg:oauth:2.0:oob. You need to authorize it in you tenant (eg build a small ap

How to test SMTP using Opportunistic TLS with Powershell and grab the public certificate a SMTP server is using

Most email services these day employ Opportunistic TLS when trying to send Messages which means that wherever possible the Messages will be encrypted rather then the plain text legacy of SMTP.  This method was defined in RFC 3207 "SMTP Service Extension for Secure SMTP over Transport Layer Security" and  there's a quite a good explanation of Opportunistic TLS on Wikipedia  https://en.wikipedia.org/wiki/Opportunistic_TLS .  This is used for both Server to Server (eg MTA to MTA) and Client to server (Eg a Message client like Outlook which acts as a MSA) the later being generally Authenticated. Basically it allows you to have a normal plain text SMTP conversation that is then upgraded to TLS using the STARTTLS verb. Not all servers will support this verb so if its not supported then a message is just sent as Plain text. TLS relies on PKI certificates and the administrative issue s that come around certificate management like expired certificates which is why I wrote th
All sample scripts and source code is provided by for illustrative purposes only. All examples are untested in different environments and therefore, I cannot guarantee or imply reliability, serviceability, or function of these programs.

All code contained herein is provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.