Sample code to check if a Folder exists else create it (SharePoint Online / PnP Framework / C#)


Sharing the sample code that we can use for checking if a particular folder exists and if not then create it in SharePoint Online. It uses PnP Framework and Azure Ad App only permissions.

Here we have used the PnP Framework Library in our console application.

We will be checking for and creating the folder at the below location.

A screenshot of a computer

Description automatically generated

On a successful run, we can see the folder named “MyFolder” created the document library.

Below is the sample code

static void Main(string[] args)
        {
            var siteUrl = "https://w72tk.sharepoint.com/sites/MyTeamSite";          
            var applicationId = "d7eaeeb7-ef0a-474d-9b94-567013576c14";
            var password = "xyz";
            var domain = "w72tk.onmicrosoft.com";
            var certPath = @"C:\SharePointApp\MyTestCertificate.pfx";

            var authManager = new AuthenticationManager(applicationId, certPath, password, domain);
            var foldertoCheck = "MyFolder";
            using (var clientContext = authManager.GetContext(siteUrl))
            {
                var currentWeb = clientContext.Web;
                var folderExists = currentWeb.DoesFolderExists(foldertoCheck);
                if (!folderExists)
                {
                    var list = clientContext.Web.Lists.GetByTitle("Documents");
                    list.RootFolder.Folders.Add(foldertoCheck);
                    clientContext.ExecuteQuery();
                }
            }
        }

Refer for more details – https://nishantrana.me/2024/07/30/calling-sharepoint-online-api-using-azure-ad-app-only-permissions-using-certificate-auth/

Hope it helps..

Advertisements

Calling SharePoint Online API using Azure AD App-Only permissions using Certificate Auth


Below are the steps we need to follow to call SharePoint Online API through a Console App (C#).

The first step is to register an Azure AD app.

Provide appropriate SharePoint API Permissions

A screenshot of a computer

Description automatically generated

As the console app would run in the background we opted for Application Permissions.

A screenshot of a computer screen

Description automatically generated

For testing, we opted for Sites.FullControl.All permission.

Also, Grant the admin consent

A screenshot of a computer

Description automatically generated

Next, we need to generate and upload the certificate

Run the below PowerShell script to generate the self-signed certificate

.\Create-SelfSignedCertificate.ps1 -CommonName “MySampleCertificate” -StartDate 2024-01-01 -EndDate 2026-10-01

#Requires -RunAsAdministrator
<#
.SYNOPSIS
Creates a Self Signed Certificate for use in server to server authentication
.DESCRIPTION
.EXAMPLE
.\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21
This will create a new self signed certificate with the common name "CN=MyCert". During creation you will be asked to provide a password to protect the private key.
.EXAMPLE
.\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21 -Password (ConvertTo-SecureString -String "MyPassword" -AsPlainText -Force)
This will create a new self signed certificate with the common name "CN=MyCert". The password as specified in the Password parameter will be used to protect the private key
.EXAMPLE
.\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21 -Force
This will create a new self signed certificate with the common name "CN=MyCert". During creation you will be asked to provide a password to protect the private key. If there is already a certificate with the common name you specified, it will be removed first.
#>
Param(

[Parameter(Mandatory=$true)]
   [string]$CommonName,

[Parameter(Mandatory=$true)]
   [DateTime]$StartDate,

[Parameter(Mandatory=$true)]
   [DateTime]$EndDate,

[Parameter(Mandatory=$false, HelpMessage="Will overwrite existing certificates")]
   [Switch]$Force,

[Parameter(Mandatory=$false)]
   [SecureString]$Password
)

# DO NOT MODIFY BELOW

function CreateSelfSignedCertificate(){

#Remove and existing certificates with the same common name from personal and root stores
    #Need to be very wary of this as could break something
    if($CommonName.ToLower().StartsWith("cn="))
    {
        # Remove CN from common name
        $CommonName = $CommonName.Substring(3)
    }
    $certs = Get-ChildItem -Path Cert:\LocalMachine\my | Where-Object{$_.Subject -eq "CN=$CommonName"}
    if($certs -ne $null -and $certs.Length -gt 0)
    {
        if($Force)
        {

foreach($c in $certs)
            {
                remove-item $c.PSPath
            }
        } else {
            Write-Host -ForegroundColor Red "One or more certificates with the same common name (CN=$CommonName) are already located in the local certificate store. Use -Force to remove them";
            return $false
        }
    }

$name = new-object -com "X509Enrollment.CX500DistinguishedName.1"
    $name.Encode("CN=$CommonName", 0)

$key = new-object -com "X509Enrollment.CX509PrivateKey.1"
    $key.ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
    $key.KeySpec = 1
    $key.Length = 2048
    $key.SecurityDescriptor = "D:PAI(A;;0xd01f01ff;;;SY)(A;;0xd01f01ff;;;BA)(A;;0x80120089;;;NS)"
    $key.MachineContext = 1
    $key.ExportPolicy = 1 # This is required to allow the private key to be exported
    $key.Create()

$serverauthoid = new-object -com "X509Enrollment.CObjectId.1"
    $serverauthoid.InitializeFromValue("1.3.6.1.5.5.7.3.1") # Server Authentication
    $ekuoids = new-object -com "X509Enrollment.CObjectIds.1"
    $ekuoids.add($serverauthoid)
    $ekuext = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1"
    $ekuext.InitializeEncode($ekuoids)

$cert = new-object -com "X509Enrollment.CX509CertificateRequestCertificate.1"
    $cert.InitializeFromPrivateKey(2, $key, "")
    $cert.Subject = $name
    $cert.Issuer = $cert.Subject
    $cert.NotBefore = $StartDate
    $cert.NotAfter = $EndDate
    $cert.X509Extensions.Add($ekuext)
    $cert.Encode()

$enrollment = new-object -com "X509Enrollment.CX509Enrollment.1"
    $enrollment.InitializeFromRequest($cert)
    $certdata = $enrollment.CreateRequest(0)
    $enrollment.InstallResponse(2, $certdata, 0, "")
    return $true
}

function ExportPFXFile()
{
    if($CommonName.ToLower().StartsWith("cn="))
    {
        # Remove CN from common name
        $CommonName = $CommonName.Substring(3)
    }
    if($Password -eq $null)
    {
        $Password = Read-Host -Prompt "Enter Password to protect private key" -AsSecureString
    }
    $cert = Get-ChildItem -Path Cert:\LocalMachine\my | where-object{$_.Subject -eq "CN=$CommonName"}

Export-PfxCertificate -Cert $cert -Password $Password -FilePath "$($CommonName).pfx"
    Export-Certificate -Cert $cert -Type CERT -FilePath "$CommonName.cer"
}

function RemoveCertsFromStore()
{
    # Once the certificates have been been exported we can safely remove them from the store
    if($CommonName.ToLower().StartsWith("cn="))
    {
        # Remove CN from common name
        $CommonName = $CommonName.Substring(3)
    }
    $certs = Get-ChildItem -Path Cert:\LocalMachine\my | Where-Object{$_.Subject -eq "CN=$CommonName"}
    foreach($c in $certs)
    {
        remove-item $c.PSPath
    }
}

if(CreateSelfSignedCertificate)
{
    ExportPFXFile
    RemoveCertsFromStore
}

Specify the password and note it down as it will be used for connection.

Upload the certificate to the Azure AD App registered.

A screenshot of a computer

Description automatically generated

For the console app, we installed the PnP.Framework Nuget Package

A screenshot of a computer

Description automatically generated

The sample code –

           try
            {
                var authManager = new AuthenticationManager(applicationId, certPath, password, domain);
                using (ClientContext clientContext = authManager.GetContext(siteUrl))
                {
                    var folder = clientContext.Web.GetFolderByServerRelativeUrl(folderRelativeUrl);
                    clientContext.Load(folder);
                    clientContext.Load(folder.Files);
                    clientContext.ExecuteQuery();

                    foreach (var file in folder.Files)
                    {
                        if (countFilesToImport < maxFilesPerCycle)
                        {
                            _filesToImport.Add(file);
                        }

                        countFilesToImport++;
                    }
                }
            }
            catch (Exception ex)
            {
                System.Console.WriteLine("Error: " + ex.Message);
                if (ex.InnerException != null)
                {
                    System.Console.WriteLine("Inner Exception: " + ex.InnerException.Message);
                }
            }
        }

We can see our app is successfully connected.

A computer screen shot of a program

Description automatically generated

The other option is to use the SharePoint app-only, which is not recommended by Microsoft.

We can see the following message for it.

Starting April 2, 2026, Azure Access Control service (ACS) usage will be retired for SharePoint in Microsoft 365 and users will no longer be able to create or use Azure ACS principals to access SharePoint. Learn more about the Access Control retirement

A screenshot of a computer

Description automatically generated

Get all the details here

Hope it helps..

Advertisements

Auto-create SharePoint Folder (SharePoint Document Location) on record create using Power Automate Flow (Dataverse)


We recently had a requirement to create the SharePoint folder automatically on the Account records creation, instead of having the users click on the Documents tab to create it.

We could write a Power Automate flow to achieve this.

Here we have enabled the Share Point server-based integration. Also, the SharePoint Site and the main or parent folder for the Account are already created/configured.

Below is the corresponding Document location record for the parent Account folder.

  • So basically, we will create a new folder inside the Account folder in the SharePoint site configured in CRM.
  • Then create a new SharePoint Document Location record specifying the Account document location as a parent.
  • And then Associate / Relate the new account record created with this new document location record.

Below is how our Flow will look like –

  1. Triggers on Account record creation.

2. Create a new folder inside Account Folder, here for the name of the folder we are using the format “AccountName_AccountNumber

3. Create a new Document Location record, associating the Account document location with the parent location record. Here we are specifying the same folder name in the relative URL.

We can also fetch the GUID of the Account document location and/or use a variable to store the GUID.

4. Lastly, we are associating these 2 new records.

Below is our newly created account record.

Below we can see our flow ran successfully.

We can see the new folder created and associated.

Here if we click the Documents tab immediately after creating the account record, before our flow runs, we might have the OOB folder also getting created along with our custom folder (flow being asynchronous). If our custom folder is already created and associated before the user clicks on the documents tab, then CRM will not be creating a new folder.

Hope it helps..

Advertisements

How to – Upload a file by URL to SharePoint (Power Automate / Dataverse)


Recently we had a requirement to upload a document (pdf) to a SharePoint folder.

We had the URL of the field saved in one of the fields of the Contact table in Dynamics 365 / Dataverse.

Below is our field and the URL in it. (the URL points to the Manual of Fulla 2, one among many awesome products by Schiit)

Below is how our flow looks like

Here we already have Server-Based SharePoint integration enabled. https://nishantrana.me/2023/07/28/based-on-entity-behavior-for-sharepoint-folder-dynamics-365-dataverse/

The flow triggers when there is an update in the File URL field of the contact record.

Next, we are using HTTP action to use the GET method to get the content of the file. As it is a public URL we haven’t specified any Authentication.

Next, we are using Create File action of SharePoint, we have specified the Site Address, Folder Path, File Name, and most importantly the Body of the HTTP action to the File Content property.

On the successful run of the flow,

we can see the document (pdf) uploaded in the SharePoint folder.

Hope it helps..

Advertisements

Based on entity behavior for SharePoint Folder – Dynamics 365 / Dataverse


After enabling Server-Based SharePoint Integration,

inside Document Management Settings, we can specify folder structure to be based on the entity either Account or Contact.

Here we have opted for Account-based first.

We can see the following folders created on the SharePoint site, that take the table or entity name.

And the records folder for Accounts created with AccountName_GUID format.

And if we open any Contact record >> Files / Documents tab, that will create the account parent folder (if not already created) and the contact folders inside it as shown below.

And if we create the contact record without having an account associated

That will create the folder for the record inside Contact Folder without the Parent Account folder created. (as there was no account associated)

For Case Records, we have the case folders created inside Account folders.

Now let us select the Contact based structure

The contact records are created inside the Contact folder, the account associated is not considered.

For Case also it is same.

For Building, a custom table, which is a child of Contact, we have the Building folder created inside the Contact folder.

Now let us keep Based on entity option unchecked

This creates the corresponding record’s folder inside the table parent folder.

Account –

Contact –

Check some of the interesting articles on SharePoint and Dynamics 365 Integration.

Hope it helps..

Advertisements

Approval / When a file is created (properties only) – when a file is added to the folder in the SharePoint library


Recently we had a requirement to implement an approval workflow when a document is added to a SharePoint library.

Below is how we can implement it.

Details –

Trigger – When a file is created (properties only)

Specify the Site Address, Library Name, and the Folder on which the flow should run.

Initialize Variable

Initialize a variable VarComments to save all the responses.

Start and wait for an approval

Here we have used the Approval type as Custom Response – Wait for one response, we could also Approve / Reject – First to respond or any approval type based on the requirements.

The Assigned to field contains the name of the user who needs to review it.

The item link contains the link to the item.

Enable reassignment as Yes will allow the approver to reassign the approval to another user from the Approval Center.

Apply to each

Append the approval response to the variable VarComments.

Condition

If Outcome is Approved

If Yes – Update File Properties

Update the properties of the file using the ID.

Update the Approval Comments property with the variable.

Update the status value as Approved.

Repeat the same step for any other Outcome.

Check other blog posts on Approvals – https://nishantrana.me/2020/08/31/approvals-power-automate-dynamics-365/

Also check –https://manueltgomes.com/reference/power-automate-trigger-reference/when-a-file-is-created-properties-only-trigger/

Hope it helps..

Advertisements