This blog will show step by step how to create an Azure Function App to upload files to SharePoint using C#.
This code will receive a public URL that contains a file to download, and we will upload this file to SharePoint, using the Graph API – and by the way, this function was created by our colleague Andhony Hernandez for a real use case we recently worked on!
Ok, let’s get into it!
But first, this is an example of how the request will look:
First, let’s define the request structure.
{function-endpoint}/api/DownloadAndUploadFunction?code={code}&fileName={file-name}&url={public-url}
We will receive 3 query parameters:
- Code: this is to authenticate our request, not required if your function is not set up to have authentication
- fileName: which name we will save the file as when uploaded to SharePoint
- url: public URL that contains the file we want to upload.Steps
We need to complete the following steps to have our function up and running.
- Register an application in the Azure portal that grants you access to upload files to SharePoint.
- Obtain Site ID, Library ID (drive id) and folder ID (item ID) where your files will be stored.
- Create your function code in Visual Studio.
- Create Function App in the Azure portal and publish your code.
Table of Contents
Register an application in the Azure portal that grants you access to upload files to SharePoint.
Go to https://portal.azure.com/, and navigate to App registrations
- Click on New registration
Assign a name to the registration and select “Accounts in this organizational directory only”.
- Go to API Permissions and click on Add permission
- In the next panel, select Microsoft Graph
- Click on Application permissions
- In the search bar, find Files.ReadWrite.All, and select it from the results.
- Once added to the list, click on Grant admin consent for [your company name]
- Go to the Certifications & secrets blade. Select Client Secrets
- Click on New client secret, assign a name and define an expiration policy
- Once it’s added, make sure to copy the text under “Value” and store it somewhere safe.
- Now, go to the Overview tab and get your tenant ID and client ID
You should have the following values at this point:
Client secret
KI68Q~nnY8FojDXub.s1bPnE.6oFeF49LX59Cbip
Client id
a3def885-2bab-4e8f-8fb7-f47f7f2e9e22
tenant id
3131a2ad-2e72-49aa-94fd-552b936e518c
Obtain Site ID, Library ID (drive id) and folder ID (item ID) where your files will be stored
To upload files to SharePoint you need
- Which site you will upload to (site id)
- Which library (drive id)
- Which folder (item id)
We can use the Graph API or Power Automate to obtain these IDs. Below we are using Postman to get them.
Authentication for the Graph API works with a Bearer token.
- First, let’s get the code. Use the same values we grabbed from step 1.
https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token
- Get Site ID using below endpoint
https://graph.microsoft.com/v1.0/sites/
- Now, let’s obtain drive id by listing all libraries under the site.
For our example, we will upload the files into a library called “Upload Files Library”
https://graph.microsoft.com/v1.0/sites/{site-id}/drives/
- Finally, let’s obtain the folder item ID where we will upload files. In this case, we will upload to folder “PDF Files”
https://graph.microsoft.com/v1.0/sites/{site-id}/drives/{drive-id}/root/children
We now have everything we need to create our function!
Create your function code in Visual Studio
First, create an azure function project and use the HTTP Trigger
Now, let’s write some code. We need a function to authenticate and connect to SharePoint.
private static async Task GetAccessToken()
{
// Azure App registration details
string clientId = "a3def885-2bab-4e8f-8fb7-f47f7f2e9e22";
string clientSecret = "KI68Q~nnY8FojDXub.s1bPnE.6oFeF49LX59Cbip";
// login url - replace 3131a2ad-2e72-49aa-94fd-552b936e518c by your tenant ID
string authority = "https://login.microsoftonline.com/3131a2ad-2e72-49aa-94fd-552b936e518c/oauth2/v2.0/token";
string resource = "https://graph.microsoft.com/.default";
// Get access token
var clientCredentials = new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(clientId, clientSecret);
var cca = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authority))
.Build();
var result = await cca.AcquireTokenForClient(new[] { resource })
.ExecuteAsync();
return result.AccessToken;
}
We need the following variables in our doe
// Get Query parameters
string url = req.RequestUri.ParseQueryString()["url"];
string fileName = req.RequestUri.ParseQueryString()["fileName"];
// Site, Drive and Folder files will be uploaded to
string siteID = "wearepowergi.SharePoint.com,c2ce9ca8-038c-46e7-9440-db1424939b13,282fa06a-5334-4abb-a089-75dbbe2664f4";
string driveID = "b!qJzOwowD50aUQNsUJJObE2qgLyg0U7tKoIl1274mZPTpI8MqmbRXRoFGWf9LCutP";
string parentID = "017XKR7NA7ULDT3MDJINEL6MLJW56AEFWG";
// variable to get file content
byte[] fileBytes;
Code to extract file content from URL and upload to SharePoint
using (HttpClient client = new HttpClient())
{
try
{
//get file fom URL and assign to variable
client.Timeout = TimeSpan.FromMinutes(3);
fileBytes = await client.GetByteArrayAsync(url);
log.LogInformation($"Downloaded bytes: {fileBytes.Length}");
}
catch (Exception ex)
{
//if any error happens
log.LogError($"Error: {ex.Message}");
log.LogError($"Error: {ex.Data}");
log.LogError($"Error: {ex.Source}");
return new StatusCodeResult((int)HttpStatusCode.InternalServerError);
}
}
// Checks if content is not empty
if (fileBytes == null || fileBytes.Length == 0)
{
log.LogError("File content is empty");
return new BadRequestObjectResult("File content is empry.");
}
// endpoint and SharePoint link
string SharePointUrl = $"https://graph.microsoft.com/v1.0/sites/{siteID}/drives/{driveID}/items/{parentID}:/{fileName}:/content";
//call GetAccessToken to obtain access token
string accessToken = await GetAccessToken();
using (HttpClient client = new HttpClient())
{
//start call to graph API
client.Timeout = TimeSpan.FromMinutes(20);
// Set Authorization header
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}");
// Add file content to request body
ByteArrayContent content = new ByteArrayContent(fileBytes);
// set headers
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/plain");
// call graph API, with all correspondin parameters
HttpResponseMessage response = await client.PutAsync(SharePointUrl, content);
response.EnsureSuccessStatusCode();
}
log.LogInformation("File downloaded and stored in SharePoint");
return new OkObjectResult("File downloaded and stored in SharePoint");
Final code should look like this
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Identity.Client;
public static class DownloadAndUploadFunction
{
[FunctionName("DownloadAndUploadFunction")]
public static async Task Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
HttpRequestMessage req,
ILogger log)
{
// Get Query parameters
string url = req.RequestUri.ParseQueryString()["url"];
string fileName = req.RequestUri.ParseQueryString()["fileName"];
// Site, Drive and Folder files will be uploaded to
string siteID = "wearepowergi.SharePoint.com,c2ce9ca8-038c-46e7-9440-db1424939b13,282fa06a-5334-4abb-a089-75dbbe2664f4";
string driveID = "b!qJzOwowD50aUQNsUJJObE2qgLyg0U7tKoIl1274mZPTpI8MqmbRXRoFGWf9LCutP";
string parentID = "017XKR7NA7ULDT3MDJINEL6MLJW56AEFWG";
// variable to get file content
byte[] fileBytes;
using (HttpClient client = new HttpClient())
{
try
{
//get file fom URL and assign to variable
client.Timeout = TimeSpan.FromMinutes(3);
fileBytes = await client.GetByteArrayAsync(url);
log.LogInformation($"Downloaded bytes: {fileBytes.Length}");
}
catch (Exception ex)
{
//if any error happens
log.LogError($"Error: {ex.Message}");
log.LogError($"Error: {ex.Data}");
log.LogError($"Error: {ex.Source}");
return new StatusCodeResult((int)HttpStatusCode.InternalServerError);
}
}
// Checks if content is not empty
if (fileBytes == null || fileBytes.Length == 0)
{
log.LogError("El contenido del archivo es nulo o vacío. No se realizará la carga en SharePoint.");
return new BadRequestObjectResult("El contenido del archivo es nulo o vacío. No se realizará la carga en SharePoint.");
}
// endpoint and SharePoint link
string SharePointUrl = $"https://graph.microsoft.com/v1.0/sites/{siteID}/drives/{driveID}/items/{parentID}:/{fileName}:/content";
//call GetAccessToken to obtain access token
string accessToken = await GetAccessToken();
using (HttpClient client = new HttpClient())
{
//start call to graph API
client.Timeout = TimeSpan.FromMinutes(20);
// Set Authorization header
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}");
// Add file content to request body
ByteArrayContent content = new ByteArrayContent(fileBytes);
// set headers
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/plain");
// call graph API, with all correspondin parameters
HttpResponseMessage response = await client.PutAsync(SharePointUrl, content);
response.EnsureSuccessStatusCode();
}
log.LogInformation("File downloaded and stored in SharePoint");
return new OkObjectResult("File downloaded and stored in SharePoint");
}
private static async Task GetAccessToken()
{
// Azure App registration details
string clientId = "a3def885-2bab-4e8f-8fb7-f47f7f2e9e22";
string clientSecret = "KI68Q~nnY8FojDXub.s1bPnE.6oFeF49LX59Cbip";
// login url - replace 3131a2ad-2e72-49aa-94fd-552b936e518c by yout tenant ID
string authority = "https://login.microsoftonline.com/3131a2ad-2e72-49aa-94fd-552b936e518c/oauth2/v2.0/token";
string resource = "https://graph.microsoft.com/.default";
// Get access token
var clientCredentials = new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(clientId, clientSecret);
var cca = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authority))
.Build();
var result = await cca.AcquireTokenForClient(new[] { resource })
.ExecuteAsync();
return result.AccessToken;
}
}
Create Function App in the Azure portal and publish your code.
Create the resource in Azure for the Azure Function. Below is an example how to set up the resource
Follow the instructions in the next screens, once you reach the Service Dependencies section, make sure that you select the storage account where your PDF documents to merge are saved. Also, double check that the right container name is used in the connection string.
In below screenshot you can see how I selected storageaccountazure8721 as the one that contains my “temp-pdf” container. Note that this container can be the same where your Function is storing its files or it can a completely different. Click publish and wait for the success confirmation from Visual Studio. Now you can call your function using the function endpoint and the query parameters!