Developing an On-premises Web Add-in

A customer I was working with wanted help developing a fully on-premises Outlook web add-in. By this, they wanted no part of it to reach out to the Internet (Azure or Office 365). They wanted:

  • To connect to their internal Exchange server
  • An internal IIS website
  • And no references to the Internet (including the Office.js).

This is the topology we are trying to achieve:

topology

If you have developed an Office Web Add-in lately, you find it is inherently biased to the Internet. Even the samples and solutions provided assume Office 365/Exchange and Azure websites. In a default, new Visual Studio solution, the links to the Office.js libraries and stylesheets are all pointing to the web. And, so as you might expect, there are some challenges to getting it to work on-premises only.

This posting covers what you must do to get such a solution to work, including getting past some pitfalls.

  1. First, you have to download the Office.js files locally. And especially for Outlook because the Office.js files that are provided by default in your solution folder (“for offline debugging” as part of VS2015U3 or earlier) are missing some features to work with specific builds of Outlook 2013 and Outlook 2016. You will run into some strange “type” missing and “Office not defined” errors if you forget this step.
  2. Once you have downloaded the Office.JS files, you will delete all the files under the Scripts\Office\1 folder and copy in the contents you downloaded in step 1.
  3. Next, find all your HTML pages where you have the following reference:
    <script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>;
    
  4. Comment out that line and add the following two lines:
    <!-- <script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript></script> -->
    <script src="../../Scripts/Office/MicrosoftAjax.js" type="text/javascript"></script>;
    <script src="../../Scripts/Office/1.1/office.js" type="text/javascript"></script>;
    
  5. Once you have developed your solution, you must setup your IIS server. In general here is what you must do:
    • The IIS Server must have ASP.NET installed, it must have .NET4 installed and you must have the Web Application role enabled.
    • Open IIS Manager
    • Create a site, and figure the folder path
    • Convert the site to an Application
    • Apply an SSL certificate that is already trusted on all your client computers or that has a root certificate authority that is trusted on all your client computers. If you browse to your site using HTTPS and you get a RED warning about an untrusted site, then the certificate is not trusted or properly setup.
  6. Next, and this was a major issue to troubleshoot, your Exchange Web Services certificate cannot be expired. If it is, any EWS call you make will return “succeeded” but will be blank – missing data. Digging into the logs you might find an error: “ErrorInvalidClientAccessTokenRequest” The Microsoft Exchange Server Auth Certificate that is used for OAuth needs to be updated. To do this you have to be logged into the Exchange server as administrator:
    • Run this cmdlet to identify the thumbprint of the certificate being used for OAUTH:Get-AuthConfig | FT currentcertificate*
    • Run this cmdlet to identify the thumbprint of certificates used for other Exchange servers (IIS, SMTP, etc.):Get-ExchangeCertificate | Fl *thumb*
    • Run these cmdlets to configure Exchange to use the valid certificate (copy/paste the thumbprint from the Get-ExchangeCertificate output):$today = Get-Date
      Set-AuthConfig -NewCertificateThumbprint newthumbprint -NewCertificateEffectiveDate $today -Force
    • Run this cmdlet to make sure the changes are published to the environmentSet-AuthConfig -PublishCertificate
    • Run this cmdlet to verify the certificate thumbprintGet-AuthConfig | FT currentcertificate*,previouscert*
  7. Now you can deploy your solution to IIS, update your manifest to point to the IIS server (do not forget the HTTPS) and then install it on the Exchange Server Control Panel (ECP) under the Organization / Add-ins option as a mandatory add-in.
  8. Finally, and this last bit is important: IF USING OUTLOOK 2013 or OUTLOOK 2016, YOU MUST BE LOGGED INTO WINDOWS ON THE SAME DOMAIN AS YOUR EMAIL. I know, I know… for some folks this sucks. I have reported this to our product team and they are looking into it. If you are not logged into the same domain controller as your email address is registered, you will not see the advertised add-in. It will load in Outlook Web Access (OWA), but will not appear in Outlook 2013/2016. The exact cause of this problem is unknown, but hopefully it will be addressed in a future version of the product (Exchange or Outlook or both).

Setting up for 100% on-premises is difficult, but it CAN be done. There are a lot of steps, but if you follow the above prescription, you should get it to work. In time, I hope to see this process get easier. But in an online world where Microsoft Office 365 and Azure are main focus, “old fashioned” on-premises solutions are going to require a little more elbow grease.

NOTE: This entry was contributed to by Arthel Bibbens (MSFT) / Exchange PFE. You can follow his posts on this topic here:

 1-GetOrgConfig Administering Office Add-ins within Exchange 2013 and Exchange 2016

blogs.technet.microsoft.com

I recently worked with a developer to deploy an Office add-in within an Exchange 2013 on-premises environment. This project highlighted a capability of Exchange and Outlook that is a huge shift in the way mail add-ins are developed, deployed, and maintained. Let’s take a look at the key components of Exchange 2013 that support this…

 

Debugging an Office Web Add-in in Production

It is sometimes tough to determine what is happening in a production environment and you need to get logging information from the add-in to see what is happening. How can you do that?

One way is to build a console.log() option into your add-in that looks for a Debug flag in the manifest. So, you will create two manifest, one that enabled Debugging and another than disables it. More on that in a bit. To start, here is the basic class I created in order to handle this:

/*!
 * logger JavaScript Library v1.0.1
 * http://davecra.com
 *
 * Copyright David E. Craig and other contributors
 * Released under the MIT license
 * https://tldrlegal.com/license/mit-license
 *
 * Date: 2016-08-09T12:00EST
 */
var console = (function () {
    "use strict";

    var console = {};

    console.initialize = function () {
        ///
<summary>
        /// Add a textarea/console to the bottom of the page and then setup the logger
        /// </summary>

        /// <param name="DebugMode" type="Boolean">If debug mode enabled - we show the console for logging</param>
        var debugMode = getParameterByName("Debug") == "true";
        if (debugMode) {
            // add the console to the screen
            $("body").append("<textarea id='log' style='width:100%' cols='2000' rows='7' wrap='off'></textarea>");
            $("body").append("<button id='saveLog'>Copy Log to Clipboard</button>");
            $("#saveLog").click(function () {
                var field = $("#log");
                field.select();
                document.execCommand("copy");
            });
        }

        console.log = function (msg) {
            ///
<summary>
            /// GLOBAL: Logs to the textarea on the page
            /// </summary>

            /// <param name="msg" type="string">The message to log</param>
            if (debugMode) {
                var d = new Date(Date.now());
                var current = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds();
                var data = $("#log").val();
                $("#log").val(current + " - " + msg + "\r\n" + data);
            }
        };
    }

    function getParameterByName(name) {
        ///
<summary>
        /// Get a parameter form the URL
        /// </summary>

        /// <param name="name" type="String">Name of the parameter to get from the query string</param>
        /// <returns type="String">Value of the paramater</returns>
        var url = window.location.href;
        name = name.replace(/[\[\]]/g, "\\$&");
        var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
            results = regex.exec(url);
        if (!results) return null;
        if (!results[2]) return '';
        return decodeURIComponent(results[2].replace(/\+/g, " "));
    }

    return console;
})();

To enable logging you will add the following code to your initialize:


console.initialize();
console.log("Started...");

Once initialized this will add a TEXTAREA to the bottom on the page where the log entries will be loaded. Additionally, it will place a “Copy To Clipboard” button at the bottom that when clicked will copy the contents of the TEXTAREA to the clipboard so that they can be forwarded to you as needed.

Once implemented and initialized, you can add a console.log() anywhere you want in your code to add an entry to the log. Now, how do you turn this on. What this is doing in initialize is to see if the debug flag is set in the Query String of the SourceLocation setting in the Manifest. To turn on debugging, you change the following line as such:


<DesktopSettings>
<SourceLocation DefaultValue="~remoteAppUrl/MessageRead.html?Debug=true"/>
<RequestedHeight>250</RequestedHeight>
</DesktopSettings>

That is it. From this you will be able to share two manifests with your users/administrators. The first one will be your default production manifest and the second one can be loaded if you need debugging information from the add-in.

Testing a Web Add-in with Multiple Manifest

First, let’s discuss the architecture and from an Outlook/Exchange perspective:

  1. First, you load your manifest on the Exchange server. The manifest is simply an XML file that contains pointers to your web site (on the IIS server).
  2. When you load Outlook Web Access and click the add-ins button, the Add-ins pane will appear and each application manifest you have loaded will appear in the list.
  3. When you click on one of  the add-is, the task pane will load (in Orange) and your site (located on the IIS Server) will populate in the pane.

The problem arises when you have but ONE Exchange server or ONE developer account for development and test. Developers want to be able to debug the code they are working with which typically loads  from the local instance of IIS Express (localhost). But testers need to be able to work with the latest release build to test.

So, how do you do this?

The key is in the Manifest file. If you open the Manifest file, you will see the <Id> field. This is the most important piece, but there are other areas you should/need to update as well. What you will essentially have is two copies of your manifest file:

The first file will use the default ID provided in the project, it should have a name like Developer Release and it will point to your localhost (this is default with the setup of a new Web Add-in).

<?xml version="1.0" encoding="UTF-8"?>
...
<Id>aaaaaaaa-3c56-4091-951e-18fda9e471d4</Id>
<Version>1.0.0.2</Version>
<ProviderName>David E. Craig</ProviderName>
<DefaultLocale>en-US</DefaultLocale>
<DisplayName DefaultValue="Demo Developer Release" />
...
<FormSettings>
<Form xsi:type="ItemEdit">
<DesktopSettings>
<SourceLocation DefaultValue="https://localhost:44303/AppCompose/Home/Home.html"/>
</DesktopSettings>
</Form>
</FormSettings>
...
</OfficeApp>

 

diagram - developer - manifest

The second file will have a different unique ID, a different name (like Tester Release) and it will point to your IIS website.

<?xml version="1.0" encoding="UTF-8"?>
...
<Id>bbbbbbbb-3c56-4091-951e-18fda9e471d4</Id>
<Version>1.0.0.2</Version>
<ProviderName>David E. Craig</ProviderName>
<DefaultLocale>en-US</DefaultLocale>
<DisplayName DefaultValue="Demo Tester Release" />
...
<FormSettings>
<Form xsi:type="ItemEdit">
<DesktopSettings>
<SourceLocation DefaultValue="https://TestServer/AppCompose/Home/Home.html"/>
</DesktopSettings>
</Form>
</FormSettings>
...
</OfficeApp>

 

diagram - tester - manifest

The real key is having different ID’s and publishing them separately. In Visual Studio the developers will have everything set as shown above (essentially leave everything as default). When they are ready to drop a build to the testers:

  1. Right-click on the Website project and click Publish. Follow the steps to publish the site to the Testers server.
  2. Right-click on the Manifest project and click Publish. Click the “Package the add-in” button.
  3. In the resulting dialog, enter the URL to the Testers site, This will ONLY update the URL, but not change the ID.
  4. Open the <manifest>.xml file in Notepad and then change the fields as shown above:
    • Modify the ID
    • Change the name so that you can identify which one is which
    • Verify the URL is correct.

At this point you are ready to go. And you can create MULTIPLE versions of your manifest. If for example you need one for Testers, one for Developer and then another for Pilot and yet another for experimental testing (each pointing to different IIS instances, sites or even to the Cloud (Azure). You can create as many manifests as you need this way, have them all show up in the Add-ins task pane allowing the testers/users to select the one they wish to work with.