Azure Function App: upload files to SharePoint from a public URL

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!

At our Microsoft Power platform consulting team, we often work with automation solutions that integrate seamlessly with SharePoint and Azure. This particular solution is designed to streamline file uploads efficiently.

Ok, let’s get into it!

But first, this is an example of how the request will look: 

public URL

First, let’s define the request structure. 

From vision to execution

Whether you're just starting or scaling automation, we help turn your ideas into impactful solutions.

{function-endpoint}/api/DownloadAndUploadFunction?code={code}&fileName={file-name}&url={public-url}

We will receive 3 query parameters:

  1. Code: this is to authenticate our request, not required if your function is not set up to have authentication
  2. fileName: which name we will save the file as when uploaded to SharePoint
  3. 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.

  1. Register an application in the Azure portal that grants you access to upload files to SharePoint.
  2. Obtain Site ID, Library ID (drive id) and folder ID (item ID) where your files will be stored.
  3. Create your function code in Visual Studio.
  4. 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
    Click on New registration

    Assign a name to the registration and select “Accounts in this organizational directory only”.

    Register an Application
    • Go to API Permissions and click on Add permission
    Go to API Permissions and click on Add permission
    • In the next panel, select Microsoft Graph
    Select Microsoft Graph
    • Click on Application permissions
    Click on Application permissions
    • In the search bar, find Files.ReadWrite.All, and select it from the results.
    Find Files.ReadWrite.All
    • Once added to the list, click on Grant admin consent for [your company name]
    Click On Grant Admin Consent
    • Go to the Certifications & secrets blade. Select Client Secrets
    Select Client Secrets
    • Click on New client secret, assign a name and define an expiration policy
    Click on New client secret
    • Once it’s added, make sure to copy the text under “Value” and store it somewhere safe.
    Copy The Text Under “Value”
    • Now, go to the Overview tab and get your tenant ID and client ID
    Go To The Overview Tab

    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
    				
    			
    Login
    • Get Site ID using below endpoint
    				
    					https://graph.microsoft.com/v1.0/sites/
    				
    			
    Get Site ID
    • 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/
    				
    			
    Obtain Drive ID
    • Finally, let’s obtain the folder item ID where we will upload files. In this case, we will upload to folder “PDF Files” 
    Upload To Folder “Pdf Files”
    				
    					https://graph.microsoft.com/v1.0/sites/{site-id}/drives/{drive-id}/root/children
    				
    			
    Upload To Folder “Pdf Files”

    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<string> 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<IActionResult> 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<string> 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

    Automate tasks that slow you down

    Free up your team’s time and focus on strategic work with digital and robotic automation.

    Create the resource in Azure for the Azure Function
    Now it’s time to Publish our Function to the Azure service. Right click on your solution and select the “Publish” option.
    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.

    Make sure that the container name in the connection string exists in the Storage account you select in the dependency – in this case “temp-pdf”
    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!

    Author
    Power Platform Consultant | Business Process Automation Expert
    Microsoft Certified Professional (Power Platform Consultant & Solution Architecht)