5

I have developed a .net console application which have these main characteristics :-

  1. Integrate with SharePoint online REST API, to retrieve some list items, and modify the items fields.
  2. Will run daily @1 am for example.
  3. I will host this console application inside Azure Function app.
  4. The Azure account does not have any permission on the sharepoint tenant, as the Azure account and the sharepoint online are on different domains.

so i am not sure under which account the console application will be running?

  • Will it runs under the current Azure account? if this is the case, then this will not work as the azure account is on different domain and does not have any permission on the sharepoint (and it shouldn't have)?

OR

  • I can define a service account for the Azure function app to run under it, where in this case i can define the service account to be an authorized account inside sharepoint online?

OR

  • i need to define the username/password inside the console application itself? i do not like to approach, as i will be exposing the password inside the console application. also changing the password for the username, means that we will need to update the console application accordingly..

so can anyone advice on this please? Thanks

EDIT

code for managing the console application authentication :-

using System; 

using System.Collections.Generic; 

using System.Linq; 

using System.Text; 

using Microsoft.SharePoint.Client; 



namespace O365SPProject 

{ 

    class Program 

    { 





     private class Configuration 

        { 

                public static string ServiceSiteUrl = "https://<tenant>.sharepoint.com"; 

                public static string ServiceUserName = "<user>@<tenant>.onmicrosoft.com"; 

                public static string ServicePassword = "xxxxxxxxxx"; 

        } 



                 static ClientContext GetonlineContext() 

                          { 

                                    var securePassword = new SecureString(); 

                                    foreach (char c in Configuration.ServicePassword) 

                                    { 

                                        securePassword.AppendChar(c); 

                                    } 

                                    var onlineCredentials = new SharePointOnlineCredentials(Configuration.ServiceUserName, securePassword); 

                                    var context = new ClientContext(Configuration.ServiceSiteUrl); 

                                    context.Credentials = onlineCredentials; 

                                    return context; 

                          } 





        static void Main(string[] args) 

        { 

            var ClientContext=GetonlineContext(); 

            Web web = clientContext.Web;  

            // do somethings            

        } 

    } 

}
John John
  • 1
  • 72
  • 238
  • 501
  • 1
    Hi john, you can reference the following link:https://sharepoint.stackexchange.com/questions/83985/access-the-sharepoint-online-webservice-from-a-console-application – Yuki Sep 12 '18 at 05:57
  • @YukiLou-MSFT thanks for the link. but seems i have not explained my question properly. Now I know very well that i can define the authentication inside my console application mainly inside the `ClientContext()`. but what i am asking about was to avoid defining the username and password inside the console application itself, and if we can define a service account (similar to how we authenticate a task inside the windows tasks scheduler) + i was asking by default under which account the Azure function will run? under the azure account username ? – John John Sep 12 '18 at 11:50
  • 1
    Is your Sharepoint Online linked to your Azure AD ???? – Thomas Sep 15 '18 at 06:38
  • @Thomas yes it is a sharepoint online connected to Azure AD.. but what is the importance of this ? – John John Sep 15 '18 at 23:32
  • So Both SP and your function are using the Azure AD ? If so you can use Managed Service Identity (MSI) to connect to Sharepoint. – Thomas Sep 16 '18 at 04:41
  • Even though both SP and function are using Azure AD, there are roadblocks in using MSI to connect to SharePoint. 1. With MSI, you can't manage app permissions that you need to provide for connecting to SharePoint. There can be some workarounds by finding AD objectids and going through PowerShell but don't want to suggest something that could lead to an unsupported road. 2. SharePoint online app-only scenario works only when token is acquired through Certificate. I'm not sure if MSI will use certificate or secret key. It's a documented limitation in one of MS FAQs. – Rohit Saigal Sep 16 '18 at 10:00
  • 1
    As an alternative to MSI, an explicitly registered application in Azure AD can be used for identity purpose though. There is a good blog series that describes the detailed steps. It's worth looking at, but comes with a few limitations that Microsoft has called out. I have edited the answer to include this option and compared it with others. – Rohit Saigal Sep 16 '18 at 10:05
  • @RohitSaigal thanks for the update/edit will check these further, to see which option will best suits my case – John John Sep 16 '18 at 22:17

2 Answers2

5

There are multiple parts to your question, so I'll answer it accordingly.

1. Which option out of the 3 you mentioned (or if there is a different better option :)), should you use to manage your configuration data/service account identity

OPTION 4 (similar to your option 2 with subtle difference): You should take your service account identity and configuration data out of your console application completely and pass them in through "Application Settings" for your Azure Function App.

This option is similar to the option 2 you had in your question, as you keep the information outside of console app code

I can define a service account for the Azure function app to run under it, where in this case i can define the service account to be an authorized account inside sharepoint online?

but difference is that I am not saying that you will be able to define a service account for your Azure function app to run under (because you can't control the account that Azure function will run under, Microsoft infrastructure takes care of it), instead you will pass it to your console app as a secure configuration data and your console app will use it. More on security/encryption later while comparing the options.

I actually took your console application code from question, created a console app and used it in a timer triggered Azure function to get it working. So these steps are from a working sample. I used the "Microsoft.SharePointOnline.CSOM" nuget package in my console app, and had to upload some of the dependency dlls along with exe in order for it to run. Feel free to ask for more details on doing this part if you run into issues.

Adding Application Settings - Navigate your Azure Function App and Click on "Application Settings"

enter image description here

Add Settings for all items that you want to take out of your console application and control from outside. I did it for all 3 items I saw, but this is up to you.

enter image description here

Then change your code to use these settings. I have shown the exact code changes at the end.

OPTION 5 Registering a new application in Azure AD to represent your Azure function.

  • You should register a new application in your Azure AD and use this identity to access SharePoint online.
  • You will need to grant permissions to SharePoint online for this application (NOTE: permission assignment will not be as granular or detailed as in case of your service account approach, I'll explain more while comparing the options)
  • You will need to associate a certificate with your AzureAD application to help in authentication.
  • While authenticating to SharePoint online, you will not be directly able to use the SharePointOnlineCredentials class as in your code today, but instead send the bearer token in 'Authorization' header for the http request.

Here is blog post that walks through detailed steps involved in this option 5. NOTE: This blog still leaves out the certificate details like password in function code at the end, which will not be ideal and you will need to move it out to App Settings or Azure Key Vault ideally.

2. Which account will the .NET console application run under and a Quick Comparison of all options

It's an arbitrary IIS App Pool account, as pointed out by @Mitch Stewart, other SO posts and is evident in the output I get for my function, it's exact value in my run came out to be "IIS APPPOOL\mawsFnPlaceholder0_v1 ". See the image at the bottom. You already have some good info shared on this, so I'll not repeat. Only thing I'll add is that this account will be controlled by the infrastructure hosting your function app and will be designed more towards taking care of isolation/other concerns in a shared infrastructure where many function apps can run, so trying to control/change it may not be the way to go right now.

Option 1 (from your question) - Giving permissions to an IIS app pool account for your SharePoint Online site, especially when you don't control the account may not be a good idea.

Option 2 (from your question) - It would have been better than the other 2 options you mentioned, but you can't really control this account.

Option 3 (from your question)- Embedding this information deep into your console application will be a maintenance issue as well as not the most secure option unless you start reading form a vault etc. Maintenance issues will remain no matter what you do because it's embedded in compiled code, which it shouldn't be.

Option 4 - This is better than previous 3 options, because it separates the concern of code from configuration and identity information, no recompilation needed for updates. Also note that whatever you store in App Settings configurations is encrypted by default (with good governance of key rotation) and is the recommended way. These values are decrypted only just before execution of your app and loaded into process memory. Look detailed discussion in this link, I have also given a small relevant excerpt below -

Provide documentation about encrypt/decrypt settings

enter image description here

Even with this option you could store them in a key vault and then your setting would be the URL of the key vault secret that has the actual information.

Option 5 - This option makes use of Azure AD based identity to authenticate with SharePoint Online which is good part. It does come with some additional effort and some limitations though, so you will need to consider if these limitations are acceptable or not in your scenario:

  • Permissions for SharePoint online will not be as granular/detailed as a user being given permissions from inside SharePoint Users/Groups interfaces (no site/list/folder/item level specific permissions etc). In this approach, you will give the permissions as part of setting up Azure AD application and you will only get generic options like these (shown in screenshot below) enter image description here

  • Microsoft has some well documented limitations in this scenario, which you can read here: What are the limitations when using app-only enter image description here

So overall, I would suggest you choose option 4 or option 5, or a combination of both for your implementation depending on which limitations are acceptable in your scenario.

3. Code Changes to use App Settings

Just the important Change

public static string ServiceSiteUrl = Environment.GetEnvironmentVariable("ServiceSiteUrl");
public static string ServiceUserName = Environment.GetEnvironmentVariable("ServiceUserName");
public static string ServicePassword = Environment.GetEnvironmentVariable("ServicePassword");

Full Code in a working Sample (I replaced do something with reading the title and Url for SharePoint Web object):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Client;
using System.Security;
using System.Security.Principal;

namespace O365SPProject
{
    class Program
    {
        private class Configuration
        {
            public static string ServiceSiteUrl = Environment.GetEnvironmentVariable("ServiceSiteUrl");
            public static string ServiceUserName = Environment.GetEnvironmentVariable("ServiceUserName");
            public static string ServicePassword = Environment.GetEnvironmentVariable("ServicePassword");
        }

        static ClientContext GetonlineContext()
        {
            var securePassword = new SecureString();
            foreach (char c in Configuration.ServicePassword)
            {
                securePassword.AppendChar(c);
            }

            var onlineCredentials = new SharePointOnlineCredentials(Configuration.ServiceUserName, securePassword);
            var context = new ClientContext(Configuration.ServiceSiteUrl);
            context.Credentials = onlineCredentials;
            return context;
        }

        static void Main(string[] args)
        {
            var ClientContext = GetonlineContext();
            ClientContext.Load(ClientContext.Web);
            ClientContext.ExecuteQuery();

            Console.WriteLine("This app found web title as: {0} and URL as: {1}", 
                ClientContext.Web.Title, ClientContext.Web.Url);

            Console.WriteLine("Console app is running with identity {0}", WindowsIdentity.GetCurrent().Name);
        }
    }
}

OUTPUT on executing Azure Function enter image description here

Rohit Saigal
  • 9,317
  • 2
  • 20
  • 32
3

The SharePoint REST API supports OAuth. Here's a promising article. Although, this might be a bit much for you intentions. Alternatively, you can try using basic auth (username + password). To guard against plain text passwords, you can store them in Azure Key Vault.

Edit The current user of an Azure function is the identity of the IIS app pool.

Code Logs

Mitch Stewart
  • 1,253
  • 10
  • 12
  • so going back to my question,, which point (1,2 or 3) i can use? using the current azure username OR using service account for the azure function OR doing the authentication inside the console app itself? – John John Sep 11 '18 at 09:56
  • also my question is mainly about which account the console app will be running under, and not about RESP API capabilities. now i can use the client-side object module CSOM to do the integration instead of using REST API.. i hope this clarifies my main question better. thanks – John John Sep 11 '18 at 13:55
  • ok thanks for the update.But can i modify this IIS APP pool user, to be something different from the azure account i am login to ? as i mentioned the azure account and the sharepoint online are on different tenants, so can i login to the azure using `userA@companyA.com` and then define the IIS APP Pool user to be under `Service@comanyB.com`? second point, can we have multiple app pool users under the same azure account (similar to the on-premises case)? – John John Sep 11 '18 at 14:22
  • 1
    I opened the App Service on which my function app runs and set the managed identity to use Azure AD, but still the app continues to run under the arbitrary username. I did some searching and found someone with your same question (https://stackoverflow.com/questions/44587136/how-to-get-current-user-identity-in-azure-function-with-azure-authentication). A Microsoft software engineer commented on the post stating it's on the road map. – Mitch Stewart Sep 11 '18 at 14:31
  • thanks for the reply and the link. so in this case to guarantee that my console application will run well, is to manage the authentication inside the console application code? see my edit with a sample of the code, will this work, as in this case i can specify what ever username i want is this correct? – John John Sep 11 '18 at 14:39
  • 1
    Your code looks like it'll work. I compared it to a working example https://stackoverflow.com/questions/22313548/how-do-i-pass-user-credentials-from-the-console-to-sharepoint-online. – Mitch Stewart Sep 11 '18 at 15:25
  • @Mithc i mean looks like azure function by default will run under arbitrary username. so in this case we will need to manage the authentication inside the code?? is this correct or i am missing something? – John John Sep 11 '18 at 15:28
  • I would testing using your account (assuming you have access), get it working, then test using a service account, get that working, then put the service account username and password in Azure Key Vault. – Mitch Stewart Sep 11 '18 at 15:30