1

I want to create an Azure function (C# API Generic HTTP) method that uploads a file to an Office365 Sharepoint document library.
Because OneDrive API allows me to upload large files (using daemon process & certificate auth.), I have succeeded in achieving the goal with a C# Console Application. The idea would be now to move the code into an Azure function. However, I receive an error during save/compilation.

Code:

#r "Newtonsoft.Json"

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Security.Cryptography.X509Certificates;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;

public class DriveResult
{
    [JsonProperty("@odata.id")]
    public string id { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }
}

const string appId = "<myAppId>";
const string tenantName = "<tenantName>";
const string tenantId = "<myTenantId>";


private static string GetResourceUrl()
{
    return "https://" + tenantName + ".sharepoint.com";
}

private static string GetAuthority()
{
    return "https://login.windows.net/" + tenantId + "/oauth2/authorize";
}

public static async Task<bool> Run(HttpRequestMessage req, TraceWriter log)
{
    var token = await GetAccessTokenAsync();
    return true; //temporary code
}

private static async Task<string> GetAccessTokenAsync()
{
    AuthenticationContext authenticationContext = new AuthenticationContext(GetAuthority(), false);
    string certfile = @"mykeyfile.pfx";

    X509Certificate2 cert = new X509Certificate2(certfile, "<myinsanepwd>");

    ClientAssertionCertificate cac = new ClientAssertionCertificate(appId, cert);
    var authenticationResult = await authenticationContext.AcquireTokenAsync("GetResourceUrl()", cac);
    return authenticationResult.AccessToken;
}

Project.json
{
  "frameworks": {
    "net46":{
      "dependencies": {
        "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.13.5",
          "System.Security.Cryptography.X509Certificates": "4.1.0"
      }
    }
   }
}

Function.json
{
  "bindings": [
    {
      "authLevel": "function",
      "name": "req",
      "type": "httpTrigger",
      "direction": "in"
    },
    {
      "name": "res",
      "type": "http",
      "direction": "out"
    }
  ],
  "disabled": false
}

Errors during compilation :

2016-10-22T13:05:24.625 run.csx(50,60): error CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
2016-10-22T13:05:24.625 run.csx(50,38): error CS0012: The type 'Task<>' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Threading.Tasks, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

Why is it complaining? The functions runs on .NET 4.6.

Best regards, Jens

Jens
  • 181
  • 1
  • 13

2 Answers2

2

As an alternative, if you want to read/write from/to OneDrive or in fact any other file based SaaS provider such as DropBox, GoogleDrive, Box, ... you may want to try the SaaS File trigger, input or output in Azure functions.

Here is a C# sample:

https://github.com/Azure/azure-webjobs-sdk-templates/tree/dev/Templates/SaaSFileTrigger-CSharp

Hamid Safi
  • 81
  • 1
1

Jens,

This is an issue triggered by some cases when referencing PCLs.

We have a plan for a more permanent solution, but in the meantime, you should be able to compile your function by adding explicitly references to the assemblies you need as you've done with JSON.NET. So in your case, the following:

#r "System.Runtime"
#r "System.Threading.Tasks"

Hope this helps!

Fabio Cavalcante
  • 12,328
  • 3
  • 35
  • 43
  • Hi Fabio, This indeed solves the compilation error. however, – Jens Oct 23 '16 at 07:33
  • the line X509Certificate2 cert = new X509Certificate2(certfile, ""); throws an Exception System.Security.Cryptography.CryptographicException: The system cannot find the file specified. Really strange because the file exists on the specified path (I checked using File.Exists()) P.S. I noticed the current directory of an Azure Function is set to D:\Windows\system32 – Jens Oct 23 '16 at 07:35
  • Can you try to pass an absolute path (instead of just the file name) crafted as the following: System.IO.Path.Combine(Environment.ExpandEnvironmentVariables("%HOME%"), @"site\wwwroot\\mykeyfile.pfx"); I'm opening an issue on GitHub so we can work on exposing a more convenient way to get the function directory. – Fabio Cavalcante Oct 23 '16 at 18:27
  • Also, as an alternative to working with OneDrive directly, you may want to consider Hamid's answer as well. – Fabio Cavalcante Oct 23 '16 at 18:28
  • Hi Fabio, I tried your solution but it's giving me the same error. :( I'd like to have a custom endpoint more instead of Hamid's answer. (which is alsoo great ofcourse.) The file however is retrieved correctly (the File.Exists() proves this) Could it have something to do with https://support.microsoft.com/en-us/kb/948154 ? – Jens Oct 24 '16 at 14:36
  • Jens, it is possible that this is related to the link you've shared. I'll spend some time looking at that. Since this issue is unrelated to the original problem, it might be worth having a separate question to get some more eyes on this. – Fabio Cavalcante Oct 24 '16 at 16:23
  • Agree , I will a specific issue for this. You can easily test it out in a simple function :-) – Jens Oct 25 '16 at 04:56
  • Hi @Fabio Cavalcante. I created for that issue specific question : http://stackoverflow.com/questions/40240195/compile-error-loading-certificate-in-azure-functions – Jens Oct 25 '16 at 15:10