Skip to content

Commit

Permalink
v2.4 (#206)
Browse files Browse the repository at this point in the history
* form control (#204)

Introducing a simple piece of logic that controls the Autodiscover URL Textbox availability. While validation hasn't been introduced here, this results in a noticeable/visible change when toggling the checkbox that should help stand out.

* OAuth 2.0 Support (#205)

* hyperlink support

adding hyperlink support to the General page

* Azure Tenant/App fields

Adding text box controls to define the Azure Application and Tenant IDs. Still need to bind these to settings within the Settings MP. Also includes URL to wiki for walk through.

* stock connector url

Updating the URL for stock Microsoft Exchange Connector from v3.1 to v4.0

* DLL updates for MSAL

Updating the DLL form with a label, text field for the MSAL DLL path, and a browse button for it.

* wrong text field

copy and paste of the function. forgot to update to the correct text field.

* naming convention typo

was using txMSALDLL as opposed to txtMSALDLL

* updating class properties

Introducing new class properties for Azure Client ID, Azure Tenant ID, and the Microsoft Authentication Library (MSAL) dll path.

* control bindings

Bindings for MSAL file path, Azure Tenant ID, and Azure Application/Client ID

* azure bindings

updating azure text fields for Tenant ID and Client ID to bind to AdminSettingsWizardData.cs

* wrong bindings

missed these two items per the last commit

* exchange online property

adding a boolean to save whether or not exchange online is used

* exchange online bindings

creating bindings for the checkbox for whether or not Exchange Online is used

* checkbox binding

Updating the checkbox control to the code behind data binding

* config screen

Including a screenshot for the wiki documentation

* updated screenshot

Took a slightly larger screenshot to provide a better context for the wiki

* Delete exchangeOnline.PNG

* exchange auth logic

Introducing if/else to control how to authenticate to exchange and the MSAL dll file path variable

* Exchange Online variable

variable to control the if/else for which Exchange based Authentication to use

* obtain access token

Retrieving an an OAuth token from Azure to use to authenticate against Exchange Online.

* remove MSAL setting

dropping MSAL dependency

* remove MSAL binding

dropping MSAL dependency

* remove MSAL configuration

dropping MSAL dependency

* remove MSAL textbox

dropping MSAL dependency

* removing MSAL button action

dropping MSAL dependency

* remove MSAL variable

dropping MSAL dependency

* inline notes

updating documentation on how Exchange connectivity/authentication works depending on the type of Exchange environment you connect to

* azure properties

retrieving Azure client/tenant properties for Exchange Online use

* readme thumbnail

image for the readme on oauth 2.0 support

* updating features

adding verbiage for Exchange Online via OAuth 2.0

* inline notes

updating inline version changes

* updating form title

moving version up for the title bar in the UI

* next version

updating the next build version

* formatting

trying to fix/understand nature of the diff
  • Loading branch information
AdhocAdam committed Sep 12, 2020
1 parent e085cf2 commit 1ed4b97
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 22 deletions.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added FeatureScreenshots/OAuth.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<DeploymentNextVersion>2.0.2016.10</DeploymentNextVersion>
<DeploymentNextVersion>2.0.2016.11</DeploymentNextVersion>
<DeploymentAutoIncrementVersion>True</DeploymentAutoIncrementVersion>
<DeploymentStartAction>None</DeploymentStartAction>
<DeploymentWebConsoleUrl />
Expand Down
25 changes: 24 additions & 1 deletion ManagementPack/2016/SMLets.Exchange.Connector/Settings.mpx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,29 @@
MaxLength="256"
MinLength="0"
Required="false" />

<Property ID="UseExchangeOnline"
Type="bool"
Key="false"
Required="false" />

<Property ID="AzureClientID"
Type="string"
AutoIncrement="false"
Key="false"
CaseSensitive="false"
MaxLength="256"
MinLength="0"
Required="false" />

<Property ID="AzureTenantID"
Type="string"
AutoIncrement="false"
Key="false"
CaseSensitive="false"
MaxLength="256"
MinLength="0"
Required="false" />

<!--File Paths-->
<Property ID="FilePathEWSDLL"
Expand Down Expand Up @@ -1280,4 +1303,4 @@
<Image ID="settingsIcon" Accessibility="Public" FileName="16x16.png" HasNullStream="false"/>
<Image ID="exchangeMailboxIcon" Accessibility="Public" FileName="exchangeMailbox16x16.png" HasNullStream="false"/>
</Resources>
</ManagementPackFragment>
</ManagementPackFragment>
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ class AdminSettingWizardData : WizardData
private String strWorkflowEmailAddress = String.Empty;
private Boolean boolEnableAutodiscover = false;
private String strAutodiscoverURL = String.Empty;
private Boolean boolEnableExchangeOnline = false;
private String strAzureTenantID = String.Empty;
private String strAzureAppID = String.Empty;

//paths
private String strEWSFilePath = String.Empty;
Expand Down Expand Up @@ -288,6 +291,51 @@ public String ExchangeAutodiscoverURL
}
}
}

public Boolean IsExchangeOnline
{
get
{
return this.boolEnableExchangeOnline;
}
set
{
if (this.boolEnableExchangeOnline != value)
{
this.boolEnableExchangeOnline = value;
}
}
}

public String AzureTenantID
{
get
{
return this.strAzureTenantID;
}
set
{
if (this.strAzureTenantID != value)
{
this.strAzureTenantID = value;
}
}
}

public String AzureClientID
{
get
{
return this.strAzureAppID;
}
set
{
if (this.strAzureAppID != value)
{
this.strAzureAppID = value;
}
}
}

//file attachments
public Boolean IsMaxFileSizeAttachmentsEnabled
Expand Down Expand Up @@ -2321,6 +2369,10 @@ internal AdminSettingWizardData(EnterpriseManagementObject emoAdminSetting)
this.SCSMmanagementServer = emoAdminSetting[smletsExchangeConnectorSettingsClass, "SCSMmgmtServer"].ToString();
this.WorkflowEmailAddress = emoAdminSetting[smletsExchangeConnectorSettingsClass, "WorkflowEmailAddress"].ToString();
this.ExchangeAutodiscoverURL = emoAdminSetting[smletsExchangeConnectorSettingsClass, "ExchangeAutodiscoverURL"].ToString();
try { this.IsExchangeOnline = Boolean.Parse(emoAdminSetting[smletsExchangeConnectorSettingsClass, "UseExchangeOnline"].ToString()); }
catch { this.IsExchangeOnline = false; }
this.AzureClientID = emoAdminSetting[smletsExchangeConnectorSettingsClass, "AzureClientID"].ToString();
this.AzureTenantID = emoAdminSetting[smletsExchangeConnectorSettingsClass, "AzureTenantID"].ToString();

//Autodiscover
try { this.IsAutodiscoverEnabled = Boolean.Parse(emoAdminSetting[smletsExchangeConnectorSettingsClass, "UseAutoDiscover"].ToString()); }
Expand Down Expand Up @@ -3008,6 +3060,9 @@ public override void AcceptChanges(WizardMode wizardMode)
emoAdminSetting[smletsExchangeConnectorSettingsClass, "WorkflowEmailAddress"].Value = this.WorkflowEmailAddress;
emoAdminSetting[smletsExchangeConnectorSettingsClass, "UseAutoDiscover"].Value = this.IsAutodiscoverEnabled;
emoAdminSetting[smletsExchangeConnectorSettingsClass, "ExchangeAutodiscoverURL"].Value = this.ExchangeAutodiscoverURL;
emoAdminSetting[smletsExchangeConnectorSettingsClass, "UseExchangeOnline"].Value = this.IsExchangeOnline;
emoAdminSetting[smletsExchangeConnectorSettingsClass, "AzureClientID"].Value = this.AzureClientID;
emoAdminSetting[smletsExchangeConnectorSettingsClass, "AzureTenantID"].Value = this.AzureTenantID;
emoAdminSetting[smletsExchangeConnectorSettingsClass, "CreateUsersNotInCMDB"].Value = this.CreateUsersNotFoundtInCMDB;
emoAdminSetting[smletsExchangeConnectorSettingsClass, "IncludeWholeEmail"].Value = this.IncludeWholeEmail;
emoAdminSetting[smletsExchangeConnectorSettingsClass, "AttachEmailToWorkItem"].Value = this.AttachEmailToWorkItem;
Expand Down Expand Up @@ -3297,4 +3352,4 @@ public override void AcceptChanges(WizardMode wizardMode)
this.WizardResult = WizardResult.Success;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ From BaseManagedEntity

//Create a new "wizard" (also used for property dialogs as in this case), set the title bar, create the data, and add the pages
WizardStory wizard = new WizardStory();
wizard.WizardWindowTitle = "SMLets Exchange Connector Settings v2.3";
wizard.WizardWindowTitle = "SMLets Exchange Connector Settings v2.4";
WizardData data = new AdminSettingWizardData(emoAdminSetting);
wizard.WizardData = data;
wizard.AddLast(new WizardStep("General", typeof(GeneralSettingsForm), wizard.WizardData));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@
</StackPanel>
</ScrollViewer>
</Grid>
</wpfwiz:WizardRegularPageBase>
</wpfwiz:WizardRegularPageBase>
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<wpfwiz:WizardRegularPageBase x:Class="SMLetsExchangeConnectorSettingsUI.GeneralSettingsForm"
<wpfwiz:WizardRegularPageBase x:Class="SMLetsExchangeConnectorSettingsUI.GeneralSettingsForm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfwiz="clr-namespace:Microsoft.EnterpriseManagement.UI.WpfWizardFramework;assembly=Microsoft.EnterpriseManagement.UI.WpfWizardFramework"
xmlns:smcontrols="clr-namespace:Microsoft.EnterpriseManagement.UI.WpfControls;assembly=Microsoft.EnterpriseManagement.UI.SmControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Custom="http://schemas.microsoft.com/SystemCenter/Common/UI/Wpf" mc:Ignorable="d" Width="551" Height="362">
xmlns:Custom="http://schemas.microsoft.com/SystemCenter/Common/UI/Wpf" mc:Ignorable="d" Width="551" Height="479"
xmlns:converters="clr-namespace:SMLetsExchangeConnectorSettingsUI.Validation" >

<Grid Name="ConfigurationGrid" Margin="15,25,15,10" >
<!-- header -->
Expand All @@ -14,17 +16,37 @@

<!-- configuration -->
<ScrollViewer Name="scrollViewer" Margin="10,75,0,10" CanContentScroll="True" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Left" Width="323">
<StackPanel Name="stackPanel" Orientation="Vertical" Height="232" VerticalAlignment="Top" HorizontalAlignment="Left" Width="313">
<StackPanel Name="stackPanel" Orientation="Vertical" Height="349" VerticalAlignment="Top" HorizontalAlignment="Left" Width="313">
<StackPanel.Resources>
<converters:BooleanToHiddenVisibility x:Key="boolToVis"/>
</StackPanel.Resources>
<TextBlock Margin="10,0" x:Name="lblMGMTServer" TextWrapping="Wrap" Text="Management Server name (set to 'localhost' if running script on a management server) otherwise use the hostname of a remote management server" />
<TextBox Height="25" Margin="10,0,10,10" x:Name="txtSCSMmgmtServer" Text="{Binding SCSMmanagementServer, Mode=TwoWay}" />
<TextBlock Margin="10,0,0,0" x:Name="lblWFEmailAddress" TextWrapping="Wrap" Text="Workflow Email Address" />
<TextBox Height="25" Margin="10,0,10,10" x:Name="txtWFEmailAddress" Text="{Binding WorkflowEmailAddress, Mode=TwoWay}" Custom:Validation.RegexPattern="^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$"/>
<CheckBox Name="chkUseAutoDiscover" FlowDirection="LeftToRight" IsChecked="{Binding Path=IsAutodiscoverEnabled, Mode=TwoWay}" >
<CheckBox Name="chkUseAutoDiscover" FlowDirection="LeftToRight" IsChecked="{Binding Path=IsAutodiscoverEnabled, Mode=TwoWay}" HorizontalAlignment="Left" Width="293" Checked="chkUseAutoDiscover_Checked" Unchecked="chkUseAutoDiscover_Unchecked" Margin="10,0,0,0" >
<TextBlock FlowDirection="LeftToRight" Text="Use Autodiscover" />
</CheckBox>

<Label Height="25" Padding="0" Margin="0,10,0,0" x:Name="lblAutodiscoverURL" Content="Autodiscover URL"/>
<TextBox Height="25" Margin="10,0,10,10" x:Name="txtAutodiscoverURL" Text="{Binding ExchangeAutodiscoverURL, FallbackValue='', Mode=TwoWay}" /> </StackPanel>
<Label Height="25" Padding="0" Margin="10,10,10,0" x:Name="lblAutodiscoverURL" Content="Autodiscover URL"/>
<TextBox Height="25" Margin="10,0,10,10" x:Name="txtAutodiscoverURL" Text="{Binding ExchangeAutodiscoverURL, FallbackValue='', Mode=TwoWay}" />
<StackPanel Orientation="Horizontal">
<CheckBox Name="Exchange365" Content="Office 365/Exchange Online" IsChecked="{Binding Path=IsExchangeOnline, Mode=TwoWay}" />
</StackPanel>
<StackPanel Visibility="{Binding Path=IsChecked, ElementName=Exchange365, Converter={StaticResource boolToVis}}">
<TextBlock VerticalAlignment="Top" Height="20" FontWeight="Light" FontSize="12" TextWrapping="Wrap">
<InlineUIContainer>
<TextBlock>
<Hyperlink RequestNavigate="Hyperlink_RequestNavigate" NavigateUri="https://github.com/AdhocAdam/smletsexchangeconnector/wiki/Configuration-Examples#scenario-3-exchange-onlineoffice-365-with-oauth">Help me configure Office 365 Connectivity</Hyperlink>
</TextBlock>
</InlineUIContainer>
</TextBlock>
<TextBlock Text="Azure Tenant ID" />
<TextBox x:Name="txtAzureTenantID" Text="{Binding AzureTenantID, FallbackValue='', Mode=TwoWay}" />
<TextBlock Text="Azure Application/Client ID" />
<TextBox x:Name="txtAzureClientID" Text="{Binding AzureClientID, FallbackValue='', Mode=TwoWay}" />
</StackPanel>
</StackPanel>
</ScrollViewer>
</Grid>
</wpfwiz:WizardRegularPageBase>
</wpfwiz:WizardRegularPageBase>
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,22 @@ private void WizardRegularPageBase_Loaded(object sender, RoutedEventArgs e)
{

}

//take the URL defined in the WPF and open a browser to it
private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
{
System.Diagnostics.Process.Start(e.Uri.AbsoluteUri);
}

//toggle AutoDiscover URL textbox availability
private void chkUseAutoDiscover_Checked(object sender, RoutedEventArgs e)
{
this.txtAutodiscoverURL.IsEnabled = false;
}

private void chkUseAutoDiscover_Unchecked(object sender, RoutedEventArgs e)
{
this.txtAutodiscoverURL.IsEnabled = true;
}
}
}
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,21 @@ This PowerShell script leverages the [SMlets module](https://www.powershellgalle


## So what is this for?
The stock Exchange Connector is a seperate download that enables SCSM deployments to leverage an Exchange mailbox to process updates to work items. While incredibly useful, some feel limited by its inability to be customized given its nature as a sealed management pack. This PowerShell script replicates all functionality of [Exchange Connector 3.1](https://www.microsoft.com/en-ca/download/details.aspx?id=45291), introduces a host of new features, and most importantly enables SCSM Administrators to customize the solution to their needs.
The stock Exchange Connector is a seperate download that enables SCSM deployments to leverage an Exchange mailbox to process updates to work items. While incredibly useful, some feel limited by its inability to be customized given its nature as a sealed management pack. This PowerShell script replicates all functionality of [Exchange Connector 4.0](https://www.microsoft.com/en-us/download/details.aspx?id=101579), introduces a host of new features, and most importantly enables SCSM Administrators to customize the solution to their needs.

## Who is this for?
This is aimed at SCSM administrators looking to further push the automation limits of what their SCSM deployment can do with inbound email processing. As such, you should be comfortable with PowerShell and navigating SCSM via SMlets.

## What new things can it do?
<table border="0">
<tr>
<td colspan="3"><i>OAuth 2.0 for Exchange Online (v2.4)</i></td>
</tr>
<tr>
<td width="200"><img src ="/FeatureScreenshots/OAuth.png" /></td>
<td width="auto">The connector supports OAuth 2.0 authentication via Azure AD Application registration to communicate with Exchange Online. Need help getting setup? The <a href="https://github.com/AdhocAdam/smletsexchangeconnector/wiki/Configuration-Examples#scenario-3-exchange-onlineoffice-365-with-oauth">wiki</a> has your covered.</td>
</tr>
</table>
<table border="0">
<tr>
<td colspan="3"><i>Transcribe Audio Files to Text (v2.3)</i></td>
Expand Down
45 changes: 36 additions & 9 deletions smletsExchangeConnector.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Requires: PowerShell 4+, SMlets, and Exchange Web Services API (already installe
Signed/Encrypted option: .NET 4.5 is required to use MimeKit.dll
Misc: The Release Record functionality does not exist in this as no out of box (or 3rd party) Type Projection exists to serve this purpose.
You would have to create your own Type Projection in order to leverage this.
Version: 2.4.0 = #171 - Optimization, Support for Exchange Online via OAuth 2.0 tokens
Version: 2.3.0 = #55 - Feature, Image Analysis (support for png, jpg, jpeg, bmp, and gif)
#5 - Feature, Optical Character Recognition (support for png, jpg, jpeg, bmp, and gif)
#54 - Feature, Speech to Text for Audio Files (support for wav and ogg)
Expand Down Expand Up @@ -153,21 +154,27 @@ $scsmMGMTServer = "$($smexcoSettingsMP.SCSMmgmtServer)"
$scsmMGMTCreds = $null

#define/use SCSM WF credentials
#$exchangeAuthenticationType - "windows" or "impersonation" are valid inputs here.
#$exchangeAuthenticationType - "windows" or "impersonation" are valid inputs here only with a local Exchange server.
#Windows will use the credentials that start this script in order to authenticate to Exchange and retrieve messages
#choosing this option only requires the $workflowEmailAddress variable to be defined
#this is ideal if you'll be using Task Scheduler or SMA to initiate this
#Impersonation will use the credentials that are defined here to connect to Exchange and retrieve messages
#choosing this option requires the $workflowEmailAddress, $username, $password, and $domain variables to be defined
#UseAutoDiscover = Determines whether ($true) or not ($false) to connect to Exchange using autodiscover. If $false, provide a URL for $ExchangeEndpoint
#ExchangeEndpoint = A URL in the format of 'https://<yourservername.domain.tld>/EWS/Exchange.asmx' such as 'https://mail.contoso.com/EWS/Exchange.asmx'
#UseExchangeOnline = When set to true the exchangeAuthenticationType is disregarded. Additionally on the General page in the Settings UI, the following should be set
#Use AutoDiscover should be set to false
#AutoDiscover URL should be set to https://outlook.office365.com/EWS/Exchange.asmx
$exchangeAuthenticationType = "windows"
$workflowEmailAddress = "$($smexcoSettingsMP.WorkflowEmailAddress)"
$username = ""
$password = ""
$domain = ""
$UseAutodiscover = $smexcoSettingsMP.UseAutoDiscover
$ExchangeEndpoint = "$($smexcoSettingsMP.ExchangeAutodiscoverURL)"
$UseExchangeOnline = $smexcoSettingsMP.UseExchangeOnline
$AzureClientID = "$($smexcoSettingsMP.AzureClientID)"
$AzureTenantID = "$($smexcoSettingsMP.AzureTenantID)"

#defaultNewWorkItem = set to either "ir", "sr", "pr", or "cr"
#default*RTemplate = define the displayname of the template you'll be using based on what you've set for $defaultNewWorkItem
Expand Down Expand Up @@ -3586,16 +3593,36 @@ if ($ceScripts) { Invoke-BeforeConnect }
#define Exchange assembly and connect to EWS
[void] [Reflection.Assembly]::LoadFile("$exchangeEWSAPIPath")
$exchangeService = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService
switch ($exchangeAuthenticationType)
if ($UseExchangeOnline)
{
"impersonation" {$exchangeService.Credentials = New-Object Net.NetworkCredential($username, $password, $domain)}
"windows" {$exchangeService.UseDefaultCredentials = $true}
}
if ($UseAutoDiscover -eq $true) {
$exchangeService.AutodiscoverUrl($workflowEmailAddress)
}
else {
#request an access token from Azure
$ReqTokenBody = @{
Grant_Type = "Password"
client_Id = $AzureClientID
Username = $username
Password = $password
Scope = "https://outlook.office.com/EWS.AccessAsUser.All"
}
$response = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$AzureTenantID/oauth2/v2.0/token" -Method "POST" -Body $ReqTokenBody

#instead of a username/password, use the OAuth access_token as the means to authenticate to Exchange
$exchangeService.Url = [System.Uri]$ExchangeEndpoint
$exchangeService.Credentials = [Microsoft.Exchange.WebServices.Data.OAuthCredentials]($response.Access_Token)
}
else
{
#local exchange server
switch ($exchangeAuthenticationType)
{
"impersonation" {$exchangeService.Credentials = New-Object Net.NetworkCredential($username, $password, $domain)}
"windows" {$exchangeService.UseDefaultCredentials = $true}
}
if ($UseAutoDiscover -eq $true) {
$exchangeService.AutodiscoverUrl($workflowEmailAddress)
}
else {
$exchangeService.Url = [System.Uri]$ExchangeEndpoint
}
}

#define search parameters and search on the defined classes
Expand Down

0 comments on commit 1ed4b97

Please sign in to comment.