Tag: Automation

  • How To Download a SharePoint Library Using Graph API (PowerShell)

    SharePoint is a powerful platform for managing and collaborating on documents, but downloading files from SharePoint libraries can be a tricky, especially when it comes to deal with large files or a large amount of documents. In this post, I want to show you a straightforward guide to download a SharePoint Library using PowerShell with Microsoft’s Graph API for SharePoint administrators, developers or system integrators. This tutorial will help you streamline your automation in terms of downloading SharePoint libraries. At the end, you’ll find a ready to use PowerShell script, which you can use to download the SharePoint Library using Graph API.

    If you want to download a SharePoint library using Graph API, there are certain prerequisites you must fulfil. To successfully download files from SharePoint using Graph API, you need to ensure the following:

    What do I need to download files to SharePoint using Graph API?

    • You have the sites.selected permission for an Azure Enterprise Application. Your global administrator in your organization can consent this permission.
    • You have installed the PNP PowerShell module to allow the Enterprise Application permission to download files from the specific SharePoint Site. Without this module, the Enterprise Application will not be able to access and download the files. Learn here how you can install the PNP PowerShell module:
      Connect to SharePoint with PowerShell | SharePoint Online (workplace-automation.com/)

    By fulfilling these prerequisites, you can easily download SharePoint libraries using Graph API and streamline your document management process.

    How to create the Enterprise Application to Download a SharePoint Library using Graph?

    1. Go to the Azure Active Directory Portal to create an App Registration with Sites.Selected permissions. This allows the Enterprise Application to access and download files from SharePoint.
    2. Create a credential object for the App registration.
    3. Once you have created the credentials for the App Registration, make sure to note down the credentials for future use. These credentials will be required when you are running the PowerShell script to download a SharePoint Library using Graph.

    By following these simple steps, you can create an Enterprise Application with the necessary permissions to download files from SharePoint using Graph API. You will get to know in the upcoming passage how to create the Enterprise Application step-by-step.

    Browse to Azure Active Directory Portal

    Open https://portal.azure.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade in your browser.

    If you have the global admin rights, I recommend authenticating with that account, so that you can directly grant your enterprise application the permission. Otherwise, you need to reauthenticate or ask your administrator to grant your enterprise application the permissions.

    Create an App Registration with Sites.Selected permissions

    Browse to the App Registration blade in Azure Active Directory Portal.

    Screenshot of the App registration Azure Active Directory Tenant overview

    Click on “New registration” to create a new App Registration.

    Screenshot of new enterprise app registration

    Define a meaningful name for the App that follows your organization’s standards, so that different administrators can easily recognize the purpose of the App. In this case I am using the name SP_Sites.Selected_Retail name, so that Azure Active directory administrators can recognize that the App Registration will have the permission Sites.Selected for the SharePoint Site Retail

    Browse to the “API Permissions” to grant your App Registraion the permission to download the SharePoint Library using Graph API.

    Screenshot of API Permissions link

    Click on “Add a permission”

    Screenshot of API Permissions overview to add a permission

    As we want to go with Microsoft Graph, please choose “Microsoft Graph”.

    Screenshot of the selection for APIs

    To download a SharePoint library using Graph API, it’s crucial to choose the right permission level. If you want your application to run in the background without requiring user authentication, you’ll need to select the Application permission option. In this tutorial on how to download a SharePoint library using Graph API, we’ll focus on the application permission method to automate the process of downloading files from SharePoint.

    Screenshot of API permission types

    Now add the sites.selected permission, which allows you to access contents of a single SharePoint site.

    Screenshot of Microsoft Graph Permissions for SharePoint Sites

    To grant admin consent for your tenant, you must be signed in with a user account that has global administrator privileges. However, if you don’t have these privileges ask your administrator with the global administrator role to consent permissions for the App Registration.

    Screenshot of granting of admin consent for current tenant

    Create a secret for the App Registration

    Now as you have configured the App permissions, you have to ensure, that you can authenticate with your App Registration. To configure the authentication, click on “Certificates & secrets”.

    Screenshot of certificates & secrets of App Registrations

    Now you can either upload certificates, create client secrets or create federated credentials. In this tutorial I will show you how to work with client secrets.

    Screenshot of Selection for App Registration Authentication

    Now you need to define a credential name. I choose the client and the IP Address to recognize, which Server/ Application will use the client secret.

    Screenshot of Add a client secret

    As you have added a client secret, make sure that you store the value for the secret as you are only able to see it, when you create it.

    Screenshot of exposed client secret value

    How to grant the App Registration Permissions for a SharePoint Site?

    To grant permission to your app registration, it’s essential to ensure that the PNP Module is installed on your client and that you have permission to use it. If you haven’t already installed the PNP Module, check out the documentation: Connect to SharePoint with PowerShell | SharePoint Online (workplace-automation.com/)

    Once you have confirmed that both conditions are met, you can use the following code to grant your app registration the necessary permissions to read from the site. You can find your app ID on the overview page of your app registration.

    Take the App ID from the overview page of your App registration and run the code below.

    Screenshot of App ID
    $AppID = "333d169e-7f2d-417c-b349-8498b2248802"
    $AppRegistrationName = "SP_Sites.Selected_Retail"
    $SiteURL = "https://m365x69801090.sharepoint.com/sites/Retail"
    
    Import-Module PnP.PowerShell
    
    $DisplayNameofSitePermission = "Enterprise Application $AppRegistrationName"
    
    
    Connect-PnPOnline -Url $SiteURL -Interactive
    Grant-PnPAzureADAppSitePermission -AppId $AppID -DisplayName $DisplayNameofSitePermission -Site $SiteURL -Permissions Read

    Now login with your account.

    Screenshot of interactive authentication prompt

    This is how it looks like, when the permission was granted successfully for the App Registration.

    Screenshot of granted site permission

    How to Download Files from SharePoint using Graph API?

    Now that we’ve created an app registration and granted it permission to write to a selected site, we can use it to download a SharePoint Library using Graph API. In this example, we’ll download the SharePoint Library Documents from the Retail SharePoint site. Before running the code, make sure you have adjusted the parameters and have the client secret ready, which we created in the previous steps.

    You can get the SiteID by browsing to the siteID page

    <siteurl>/_api/site/id

    For my example:

    https://m365x69801090.sharepoint.com/sites/Retail/_api/site/id

    When you run the code, you’ll be prompted to enter the client secret for your app registration.

    Screenshot of SharePoint Site ID

    Once you have replaced the parameters with your actual values, you can download aSharePoint Library with Graph using PowerShell. You will get an authentication prompt, where you have to enter the client secret for the App Registration.

    Screenshot of authentication promt for app registration

    # Script to download a SharePoint Library using Graph
    # Author: Serkar Aydin - Serkar@workplace-automation.com
    # Accept input parameters
    Param (
        $Tenant = "m365x69801090",
        $AppID = "333d169e-7f2d-417c-b349-8498b2248802",
        $SiteID = "74667e94-9fcf-41ab-8e2f-0dfaf0294de8",
        $LibraryURL = "https://m365x69801090.sharepoint.com/sites/Retail/Shared%20Documents",
        $Path = "C:\Users\Serkar\Desktop\Retail"
    )
    
    Function DownloadDriveItem {
    
        param(
            $DriveItem,
            $URL,
            $Header,
            $Path
            
        )
        
        #if there is no downloadurl, it is a folder
        If (!$DriveItem. '@microsoft.graph.downloadUrl') {
        
            Write-Output "Downloading the folder $($DriveItem.weburl)"
        
            #Create a folder for the SharePoint folder
            $FolderPath = "$Path\$($DriveItem.name)"
            New-Item -ItemType Directory -Path $FolderPath | Out-Null
    
            $Url  = "https://graph.microsoft.com/v1.0/drives/$DriveID/items/$($DriveItem.ID)/children"
            $Response =  Invoke-RestMethod -Uri $Url -Headers $Header -Method Get -ContentType 'multipart/form-data' 
    
            $Response.value | ForEach-Object {
    
                DownloadDriveItem -DriveItem $_ -URL $Url -Header $Header -Path $FolderPath
    
            }
    
        }
    
        #Else it is a file
        Else{
        
            Write-Output "Downloading the file $($DriveItem.weburl)"
            Invoke-WebRequest -Uri $DriveItem.'@microsoft.graph.downloadUrl' -OutFile "$Path\$($DriveItem.name)"
        }
    }
    
    
    # Prompt for application credentials
    $AppCredential = Get-Credential($AppID)
    
    #region authorize
    
    # Set the scope for the authorization request
    $Scope = "https://graph.microsoft.com/.default"
    
    # Build the body of the authorization request
    $Body = @{
        client_id = $AppCredential.UserName
        client_secret = $AppCredential.GetNetworkCredential().password
        scope = $Scope
        grant_type = 'client_credentials'
    }
    
    # Build the URL for the authorization request
    $GraphUrl = "https://login.microsoftonline.com/$($Tenant).onmicrosoft.com/oauth2/v2.0/token"
    
    # Send the authorization request and retrieve the access token
    $AuthorizationRequest = Invoke-RestMethod -Uri $GraphUrl -Method "Post" -Body $Body
    $Access_token = $AuthorizationRequest.Access_token
    
    # Build the header for API requests
    $Header = @{
        Authorization = $AuthorizationRequest.access_token
        "Content-Type"= "application/json"
    }
    
    #endregion
    
    #region get drives
    
    # Build the URL to retrieve the list of drives in the SharePoint site
    $GraphUrl = "https://graph.microsoft.com/v1.0/sites/$SiteID/drives"
    
    # Convert the body of the authorization request to JSON and send the API request
    $BodyJSON = $Body | ConvertTo-Json -Compress
    $Result = Invoke-RestMethod -Uri $GraphUrl -Method 'GET' -Headers $Header -ContentType "application/json"
    
    # Find the ID of the specified SharePoint library
    $DriveID = $Result.value| Where-Object {$_.webURL -eq $LibraryURL } | Select-Object id -ExpandProperty id
    
    # If the SharePoint library cannot be found, throw an error
    If ($DriveID -eq $null){
        Throw "SharePoint Library under $LibraryURL could not be found."
    }
    
    #endregion
    
    #region create folder. If there is already one, replace it with the new folder
    
    Try {
    
        New-Item -ItemType Directory -Path $Path -ErrorAction Stop | Out-Null
    }
    Catch {
    
            Remove-Item $Path -Force -Recurse
            New-Item -ItemType Directory -Path $Path -Force | Out-Null
    }
    #endregion
    
    #region download library
    
    $Url  = "https://graph.microsoft.com/v1.0/drives/$DriveID/root/children"
    $Response =  Invoke-RestMethod -Uri $Url -Headers $Header -Method Get -ContentType 'multipart/form-data' 
    
    $Response.value | ForEach-Object {
    
        DownloadDriveItem -DriveItem $_ -URL $Url -Header $Header -Path $Path
    
    }
    
    #endregion
    Screenshot of the process "Download a SharePoint Library using Graph"

    As you can see, I was able to download a SharePoint Library using Graph API. All folders and subfolders are created on my local C drive. Result of Download a SharePoint Library using Graph 1

    Result of Download a SharePoint Library using Graph 1
    Result of Download a SharePoint Library using Graph File share 1
    Result of Download a SharePoint Library using Graph 2
    Result of Download a SharePoint Library using Graph File share 2
    Result of Download a SharePoint Library using Graph 3
    Result of Download a SharePoint Library using Graph File share 3

    Further Reference

    You might want to download single files from SharePoint with Graph API? Check this out:
    How to download files from SharePoint using Graph API (PowerShell) (workplace-automation.com/)

    Learn how to access SharePoint via Graph in PowerShell: Access SharePoint via Graph API in PowerShell

    Learn how to upload files to SharePoint using Graph (PowerShell):
    How to Upload Files to SharePoint using Graph API PowerShell (workplace-automation.com/)

  • SharePoint Downloader | Comfortable file download in 2 steps

    Downloading SharePoint files might be necessary in areas, where we have lack of good network bandwith, if you want to keep working e.g. when you travel by train or plane. Downloading sharepoint files can be time consuming if you have to do it one by one. In this article, you will learn, how you can do it easily in the GUI, but also how you can download your SharePoint files with a so called SharePoint Downloader – a PowerShell script. If you are only interested in the SharePoint downloader, click here.

    Download SharePoint files in the GUI

    Before we start doing this with PowerShell, I want to show you how you download the file in the GUI. GUI stands for graphical user interface. I will show you how to download the file Opportunity2 from the SharePoint Library Opportunitylibrary. This is absolutely sufficient in our daily work routine.

    In the first step we open a SharePoint library:

    Screenshot of a SharePoint Library, where we want to download a SharePoint file

    Here we can mark a file and then download – looks easy right?

    Screenshot of a marked file to download the file

    As you can see the file is downloaded and we can keep working offline – but hey don’t forget to upload it later to SharePoint again :).

    Downloaded SharePoint file

    Download SharePoint files with PowerShell

    Scenario

    If you need to download multiple SharePoint files – this makes sense, if you need the files in another system and you need to export the files periodically, you can do it with PNP.PowerShell. In the following scenario, I will show you how to download multiple files by the example of the Library “Opportunitylibrary”, which contains two documents.

    Opportunity library with two documents I want to download

    Authentication and parameters

    In the beginning, we start with connecting to the site. If you are a beginner, you might be interested in a detailed description, which you can find here: Connect to SharePoint with PowerShell | SharePoint Online (workplace-automation.com/)

    I have exported the credential object previously to reuse it, which you also can do. This is strongly recommended if the script shall ran in the background e.g. when you want to make use it in task scheduler. Do not expose the credentials in blank text – this is very bad practise and is a security problem. If you want to make use of saved credentials like I did, check the article: Use credentials in PowerShell | SPO Scripts

    Before you run the scripts, change the parameters to your siteurl, your path, your list and your credential file. The parameters I have provided won’t work, if you don’t change them.

    param
    (
        $SiteUrl = "https://devmodernworkplace.sharepoint.com/sites/Sales",
        $DownloadPath = "C:\temp",
        $ListName = "Invoices",
        $CredPath = "C:\Users\Serkar\Desktop\devmod.key"
    )
    
    $Credential = Import-Clixml -Path  $CredPath
    Connect-PnPOnline -Url $SiteUrl -Credential $Credential

    For interactive PowerShell sessions, you can also choose this authentication mechanism. Interactive means, that a person is running the script and can interact with the PowerShell terminal. The opposite would be if you would schedule the task in the task scheduler and the task would run – let’s say once a day. For this purpose the script would ask the terminal, but since there is nobody responsing the authentication request, the script would be halted and no file would be downloaded.

    param
    (
        $SiteUrl = "https://devmodernworkplace.sharepoint.com/sites/Sales",
        $DownloadPath = "C:\temp",
        $ListName = "Invoices"
    )
    
    
    Connect-PnPOnline -Url $SiteUrl -Interactive

    Download all SharePoint files from a library with PowerShell

    If you want to download all files of a library, you have to run following script. The script iterates through all files in your SharePoint library defined in the parameter block and it will download all the files to the downloadpath you have provided in the parameter block.

    Get-PnPListItem -List $ListName  | 
            ForEach-Object { 
                Get-PnPFile -Url $_.FieldValues.FileRef -Filename $_.FieldValues.FileLeafRef  -Path $DownloadPath -AsFile 
            }

    As you can see, I could download SharePoint files with PowerShell.

    The result of download SharePoint files

    Download specific SharePoint files from a library with PowerShell

    If you want to download only specific files, you can use following script. This script is using the out-gridview cmdlet to give you the possibility to choose only specific files.

    Get-PnPListItem -List $ListName | 
        Select-Object Id,@{N="FileName";E={$_.FieldValues.FileLeafRef}}, @{N="Link";E={$_.FieldValues.FileRef}} |
            Out-GridView -PassThru |
                ForEach-Object { 
                    Get-PnPFile -Url $_.Link -Filename $_.FileName  -Path $DownloadPath -AsFile 
                }

    A grid will pop up, where you can select the documents, which you want to download. I have marked the second file and clicked on OK. If we check the folder, we can see, that the file have been downloaded!

    Screenshot of the grid view for the SharePoint files, which you can download

    Et voilà – the file, which I have marked is downloaded!

    SharePoint Downloader

    In this section I provide you my ready to run scripts. I call them SharePoint Downloader – a small tool with big impact 🙂

    SharePoint Downloader for automation: Ready-to-run script for automation

    With this script you can download SharePoint files to your specified folder. It’s purpose is for automation, means that it can run in the background with azure automation triggered by a runhook or with task scheduler triggered by a time schedule. Please change the parameters of the SharePoint Downloader and ensure, that the credential object is exported to the CredPath before scheduling the script. If you forgot how to do it, check out this article: Use credentials in PowerShell | SPO Scripts

    param
    (
        $SiteUrl = "https://devmodernworkplace.sharepoint.com/sites/Sales",
        $DownloadPath = "C:\temp",
        $ListName = "Opportunitylibrary",
        $CredPath = "C:\Users\Serkar\Desktop\devmod.key",
        $Force = $true
    )
    
    $Credential = Import-Clixml -Path  $CredPath
    Connect-PnPOnline -Url $SiteUrl -Credential $Credential
    
    #create folder if it does not exist
    If (!(Test-Path $DownloadPath))
    {
        New-Item -ItemType Directory $DownloadPath
    }
    
    If ($Force)
    {
        Get-PnPListItem -List $ListName  | 
            ForEach-Object { 
                Get-PnPFile -Url $_.FieldValues.FileRef -Filename $_.FieldValues.FileLeafRef  -Path $DownloadPath -AsFile -Force
            }
    }
    Else
    {
        Get-PnPListItem -List $ListName  | 
            ForEach-Object { 
                Get-PnPFile -Url $_.FieldValues.FileRef -Filename $_.FieldValues.FileLeafRef  -Path $DownloadPath -AsFile 
            }
    }
    

    SharePoint Downloader for interactive: Ready-to-run script for interactive sessions

    This script can be used for interactive sessions, so you need personally to start the script and not the task scheduler or azure automation. You will be asked for each parameter, when starting the script.

    param
    (
        [Parameter(
        Mandatory=$true,
        ValueFromPipelineByPropertyName=$true,
        Position=0, 
        HelpMessage="Please provide the url in this format: https://tenant.sharepoint.com/sites/sitename")
        ]
        $SiteUrl,
        [Parameter(Mandatory=$true,
        ValueFromPipelineByPropertyName=$true,
        Position=1,
        HelpMessage="Please provide path, where the downloaded files should be located at.")
        ]
        $DownloadPath,
        [switch][Parameter(Mandatory=$false,
        ValueFromPipelineByPropertyName=$true,
        Position=2,
        HelpMessage="Use -Force paramater to overwrite existing files.")
        ]
        $Force = $true
    
    )
    
    Connect-PnPOnline -Url $SiteUrl -Interactive
    
    #create folder if it does not exist
    
    If (!(Test-Path $DownloadPath))
    {
        New-Item -ItemType Directory $DownloadPath
    }
    
    
    $ListName = Get-PnPList | Out-GridView -PassThru | Select-Object Title -ExpandProperty Title
    
    If ($Force)
    {
        Get-PnPListItem -List $ListName | 
        Select-Object Id,@{N="FileName";E={$_.FieldValues.FileLeafRef}}, @{N="Link";E={$_.FieldValues.FileRef}} |
            Out-GridView -PassThru |
                ForEach-Object { 
                    Get-PnPFile -Url $_.Link -Filename $_.FileName  -Path $DownloadPath -AsFile -Force -WarningAction Stop
                    Write-Host "Downloaded $($_.FileName) Green to $DownloadPath" -ForegroundColor Green 
                }
    }
    Else
    {
        Get-PnPListItem -List $ListName | 
        Select-Object Id,@{N="FileName";E={$_.FieldValues.FileLeafRef}}, @{N="Link";E={$_.FieldValues.FileRef}} |
            Out-GridView -PassThru |
                ForEach-Object { 
                    Get-PnPFile -Url $_.Link -Filename $_.FileName  -Path $DownloadPath -AsFile -WarningAction Stop
                    Write-Host "Downloaded $($_.FileName) Green to $DownloadPath" -ForegroundColor Green 
                }
    }

    Conclusio

    With PNP.PowerShell we can download SharePoint files easily. In this article I have described, how you can download single and multiple SharePoint files with the GUI and with PowerShell. I have provided you the SharePoint Downloader – a script, which downloads all files in SharePoint library interactively or automated.

    References

    Here is the official reference from microsoft about the main cmdlet get-pnpfile: Get-PnPFile (PnP.Powershell) | Microsoft Docs

    Geschäft Foto erstellt von yanalya – de.freepik.com

  • 3 of the most important SharePoint PowerShell Modules and Snappins

    In my role as an automation consultant, I encounter several challenges, where you have to think out of the box. Thinking out of the box also means, to reconsider the tools, which you are using. If you start with SharePoint/ M365 automation, you will notice that there are at least three SharePoint PowerShell modules and snappins, with which you can automate your SharePoint environment. All of the SharePoint PowerShell modules/ snappins have indeed a reason for existence. In this article, I will show you where the focus of each module is, so you can get an idea, when to use which module. You will also get a overview, how to use all of them and what pros and cons eacht SharePoint PowerShell module/ snappin has.

    Think out of the box when designing an automation solution

    PNP PowerShell

    The beginning of PNP PowerShell was a community project of several developers, which have developed many client side object model (CSOM) wrappers for SharePoint 2013, 2016 and 2019. In fact PNP stands for Patterns and Practices. The modules are known as SharePointPnPPowerShell20xx or SharePointPnPPowerShellOnline. About the turn of the year all consolidated to PNP.PowerShell. Also the main focus from “catchall” have changed to the cloud version of SharePoint.

    When to use PNP PowerShell

    PNP.PowerShell is developing to one of the overreaching modules in Microsoft 365 cosmos. I would recommend to try everything first with PNP.PowerShell before you try doing it with SharePointOnlinePowerShell, since the development of the module goes on and on and you can do way more things with PNP PowerShell, than with SharePointOnlinePowerShell in terms of business process automation.

    Topics covered by PNP PowerShell

    Currently PNP PowerShell covers following topics and services:

    • SharePoint Online
    • SharePoint Server (on premises)
    • M365 Groups
    • Power Automate
    • Azure AD
    • SharePoint Syntex
    • Microsoft Teams
    • Microsoft Planner

    Pros and Cons of PNP PowerShell

    ProsCons
    ✅ You can use it for on-premises and cloud services❌ Some cmdlets are not working as expected e.g. Set-PNPSite -SitecollectionAdmin
    ✅ You can use it on all servers/ clients, which have access to the tenant/ farm (online or on-premises)❌ With constant development, cmdlets might change, so you have to monitor the changes, when updating the module
    ✅ Constant development of module❌ You have to consent the access with the global admin role
    ✅ Supports many branches of M365 (SharePoint, Teams, Planner etc.)❌ Microsoft Support won’t support if there is a bug in the modules. You have to open a request in github
    ✅ You don’t have to make breaking changes to your scripts if you move from SharePoint Server to SharePoint Online
    ✅ You can change a wide range of content in SharePoint (Sites, Lists, Items)
    ✅ You don’t need SharePoint Admin permission to connect to a site

    How to make use of PNP PowerShell?

    If you want to make use of PNP PowerShell, check out this blog post, which describes every step of it in detail: Connect to SharePoint with PowerShell | SharePoint Online (workplace-automation.com/)

    What can I change with PNP PowerShell?

    In this section, I want to give you an overview of what you can change with PNP Powershell. Indeed there are more objects in SharePoint. I focus in the first place on objects, which I think are mostly changed with PowerShell for business process automation.

    ObjectChanging
    possible
    with
    PNP PowerShell?
    CmdletPrequisitesReference
    TenantSet-PNPTenantSharePoint Admin RoleSet-PnPTenant (PnP.Powershell) | Microsoft Docs
    Hub SiteSet-PNPHubSite Access to SiteSet-PnPHubSite (PnP.Powershell) | Microsoft Docs
    SiteSet-PNPSiteAccess to Site Set-PnPSite (PnP.Powershell) | Microsoft Docs
    Sub SiteSet-PNPWeb Access to Site and SubsiteSet-PnPWeb (PnP.Powershell) | Microsoft Docs
    ListSet-PNPList Access to Site & ListSet-PnPList (PnP.Powershell) | Microsoft Docs
    LibrarySet-PNPSite Access to Site & Library Set-PnPList (PnP.Powershell) | Microsoft Docs
    ItemSet-PNPListItemAccess to Site & List & ItemSet-PnPListItem (PnP.Powershell) | Microsoft Docs
    FileSet-PNPListItem Access to Site & Library & FileSet-PnPListItem (PnP.Powershell) | Microsoft Docs
    PageSet-PNPage Access to Site & Library & Page Set-PnPPage (PnP.Powershell) | Microsoft Docs

    Who is maintaing PNP PowerShell?

    The module is maintained by the PNP Developer Community. At this stage I have to thank you guys for your hard and good work. It was really a game changer, when I didn’t had to use scripts on the SharePoint Server.

    Since it is all open source, you can also check the source code here: Microsoft 365 Community (github.com)

    You can find more about the community here: SharePoint Developer Community (SharePoint PnP) resources | Microsoft Docs

    What if I find a bug in PNP Powershell?

    Be sure to follow their rules, when you open an issue, so they can help you faster. In the first step check the discussions. You have to be patient, since it is a community developed module.

    If you find a bug, you have to open a github issue here: pnp/powershell: PnP PowerShell (github.com)

    SharePointOnlinePowerShell

    SharePointOnlinePowerShell is the official Module, which is published by Microsoft. In comparison to PNP Powershell, SharePointOnlinePowerShell focuses only on the administration of SharePoint Online.

    When to use SharePointOnlinePowerShell

    My strategy is to use SharePointOnlinePowerShell, when I experience a buggy behaviour by PNP.PowerShell. Altough it is the official SharePoint PowerShell module, you cannot change e.g. list items.

    Topics covered by SharePointOnlinePowerShell

    SharePointOnlinePowerShell is only focussing on SharePoint Online. You cannot connect to another service or to SharePoint Server (on premises).

    Pros and Cons of SharePointOnlinePowerShell

    ProsCons
    ✅ I have experienced more stability on this module – It tends to have less bugs❌ You can only administer SharePoint. Business process automation will be hard to cover with this module, because you cannot change webs, lists or items
    ✅ It is supported by Microsoft❌ You can use it only for SharePoint Online
    ✅ Constant development of module – I have not experienced any breaking changes❌ You have to have SharePoint admin role

    How to make use of SharePointOnlinePowerShell?

    I have described how to install and connect with this SharePoint PowerShell Module here: SharePointOnlinePowerShell: How to connect to SharePoint Online (workplace-automation.com/)

    What can I change with SharePointOnlinePowerShell ?

    In this section, I want to give you a overview of what you can change with PNP Powershell.

    ObjectChanging
    possible
    with
    SharePointOnlinePowerShell?
    CmdletPrequisitesReference
    TenantSet-SPOTenantSharePoint Admin RoleSet-SPOTenant (SharePointOnlinePowerShell) | Microsoft Docs
    Hub SiteSet-PNPHubSite SharePoint Admin RoleSet-SPOHubSite (SharePointOnlinePowerShell) | Microsoft Docs
    SiteSet-SPOSiteSharePoint Admin RoleSet-SPOSite (SharePointOnlinePowerShell) | Microsoft Docs
    Sub Site
    List
    Library
    Item
    File
    Page

    Who is maintaing SharePointOnlinePowerShell?

    The module is developed and maintained by Microsoft. You can find the source code is here: Microsoft 365 Community (github.com)

    What if I find a bug in SharePointOnlinePowerShell ?

    You can contact Microsoft Support or try to “Contact Owners” in PowerShellGallery: https://www.powershellgallery.com/packages/Microsoft.Online.SharePoint.PowerShell

    SharePointOnlinePowerShell PowerShell Gallery

    Microsoft.SharePoint.PowerShell

    Microsoft.SharePoint PowerShell is the only type of cmdlets, which is not a SharePoint PowerShell Module, but a PSSnappin. Snapins are considered as the old way to add cmdlets and other resources. It’s focus is SharePoint Server.

    When to use Microsoft.SharePoint.PowerShell

    I recommend to use Microsoft.SharePoint.PowerShell in following situations:

    • You don’t plan to migrate the automation solution to SharePoint Online
    • You have access to the SharePoint Servers
    • You want to administer SharePoint Server

    Topics covered by Microsoft.SharePoint.PowerShell

    With Microsoft.SharePoint.PowerShell you can only connect to SharePoint Server (on premises). You can see all cmdlets here from the Microsoft reference: SharePointServer Module | Microsoft Docs

    Pros and Cons of Microsoft.SharePoint.PowerShell

    ProsCons
    ✅ Microsoft.SharePoint.PowerShell is a established PSSNappin, which tends to have very few bugs❌ You have to readapt the scripts, if you want to migrate to SharePoint Online
    ✅ It is supported by Microsoft❌ You can use it only for SharePoint Server
    ✅ You can change nearly everything with this SharePoint PowerShell Module on your SharePoint Server❌ You need SPShellAdmin rights to access SharePoint Server with this SharePoint PowerShell Module

    How to make use of Microsoft.SharePoint.PowerShell?

    1. Connect to SharePoint Server with a user, which is SPShellAdmin
    2. Start Windows PowerShell
    3. Type Add-PSSnapin Microsoft.SharePoint.Powershell
    4. You are connected!

    What can I change with Microsoft.SharePoint.PowerShell?

    If you work with this SharePoint PowerShell snappin, you will notice, that you have to use mostly the SpWeb classes to change everything below a SpWeb. Check out the Microsoft docs to see, which classes are below SPWeb: SPWeb Class (Microsoft.SharePoint) | Microsoft Docs. If you need help, don’t hesitate to contact me: Serkar@workplace-automation.com

    ObjectChanging
    possible
    with
    PNP PowerShell?
    CmdletPrequisitesReference
    FarmSet-SPFarmConfigSPShellAdminAcessSet-SPFarmConfig (SharePointServer) | Microsoft Docs
    SiteSet-SPSite SPShellAdminAcess Set-SPSite (SharePointServer) | Microsoft Docs
    Sub SiteSet-SPWeb SPShellAdminAcess Set-SPWeb (SharePointServer) | Microsoft Docs
    ListNo dedicated cmdlet. You have to call lists from web object
    (get-spweb "url").lists
    SPShellAdminAcess
    Library No dedicated cmdlet. You have to call libraries from web object

    (get-spweb "url").lists
    SPShellAdminAcess
    ItemNo dedicated cmdlet. You have to call libraries from list object

    (get-spweb "url").lists.items
    SPShellAdminAcess
    FileNo dedicated cmdlet -You have to call the file from the item object

    (get-spweb "url").lists.items[0].file
    SPShellAdminAcess
    PageNo dedicated cmdlet – You have to call it from the sitepages library. SPShellAdminAcess

    Who is maintaing Microsoft.SharePoint.PowerShell?

    The module is maintained by Microsoft.

    What if I find a bug in Microsoft.SharePoint.PowerShell?

    You can contact Microsoft Support and report the bug there.

    Conclusio

    As you saw each of the SharePoint PowerShell module / snappin got its pros and cons. PNP PowerShell is the number one choice, if you want to automate processes in Microsoft Cloud. Beside this SharePointOnlinePowerShell is the second tool recommended, If you only seek to administer SharePoint Online. As a final point Microsoft.SharePoint.PowerShell should be mentioned as a powerful tool if you want to administer SharePoint Server (on-premises). If I missed a point, I would appreciate, if you contact me: serkar@workplace-automation.com.

    Further Documentation

    This is the direct link to PNP documentation: http://aka.ms/m365pnp

    PNP provide code samples, which you can find here: PnP | Microsoft 365

    You can check out the PNP repro on github here. GitHub – pnp/powershell: PnP PowerShell

  • How to filter for PowerShell objects easily

    If you are working interactively with PowerShell, It might be unhandy to define all the filtering options with Where-Object. I will show you how to filter for PowerShell objects the traditional way and doing it with Out-Gridview.

    Filter for PowerShell objects – the traditional way with Where-Object

    If we are looking forward to filter in PowerShell, we normaly make use of Where-Object this. In my example I am looking forward for all proccesses, which contain the name msedge and the value for Handles is greater than 292. My query would look like this:

    #traditional way
    Get-Process  | Where-Object {$_.processname -contains "msedge" -and $_.Handles -gt 292}
    Output of all processes in PowerShell
    Get-Process the traditional way with Where-Object

    Filter for PowerShell objects – Out-Gridview

    I want to show you how to filter for Powershell objects easily by using following cmdlet:

    Out-GridView -PassThru

    Example:

    Get-Process | Out-GridView -PassThru

    Doing that results in a grid popping up.

    output of processes as a grid in PowerShell

    Now you have multiple options.

    You can make use of the filter of the out of the box filtering options. Let’s say I just want to find the msedge processes.

    So I am adding the Processname as a filter criteria:

    filtering options in the grid in PowerShell

    Adding ProcessName eligibles setting filtering following operators:

    contains
    does not contains
    beginns with
    is equal to
    is not equal to
    ends with
    is empty
    is not empty

    Entering msedge to the contains field, shows only process entries, which contain msedge

    filtered results in PowerShell grid
    filtering for processes which contain msedge in the name

    Now you can add additional filters.

    Additional filters in PowerShell
    I do add NPM(K) for the value 19
    Filter for npmk equals 19

    We can even now sort all the stuff by clicking on the column, which we want to sort for.

    Sort for CPU in GUI
    Sorting for highest CPU usage

    Export objects to Excel

    If you mark everything with CTRL + A, you can copy all the stuff and put it e.g. straigth to Excel

    Exporting objects to Excel
    Easy way to export to excel

    If you want to process further with powershell, you can mark the entities needed and click on OK. I selected the first and the thridh entity.

    Picture selecting only two entities
    selecting only two entities

    As you see, PowerShell returns my selection

    PowerShell return

    Conclusio

    As you can see you can save time and coding lines, when use are working inteactively on PowerShell by filter forPowerShell objects with Out-Gridview.

    You might find intersting

    Filtering items with CAML. For further learnings check out the post: Filtering for SharePoint items with CAML Queries | SPO Scripts

    Official documentation of Microsoft for the cmdlet Out-Gridview: Out-GridView (Microsoft.PowerShell.Utility) – PowerShell | Microsoft Docs

  • Access SharePoint via Graph API in PowerShell

    Access SharePoint via Graph API in PowerShell

    Sometimes the use of PNP.PowerShell might not be sufficient. I encountered this experience, when I wanted to find out the usage of all sites. The Graph API provides methods, which you can use in your PowerShell Scripts. So in my example I wanted to get unused Sites with PowerShell. If you want to make use of it, you have to register an enterprise application and afterwards you can retrieve the information with an HTTP-Webrequest. In the following I will show you step by step how to access your SharePoint tenant with Graph API in PowerShell.


    Considerations – Find the right Graph API Method

    The Graph API has multiple methods, which we can use to analyze and change the content of our M365 services. In order to find the right method for your plan, check folllowing resources to see what the Graph API is capable of Microsoft Graph REST API v1.0 reference – Microsoft Graph v1.0 | Microsoft Docs. Based on the needed methods, you have to set up your enterprise application.

    Let’s assume, that you want to see the site usage of all sites in your tenant. In order to do this, you have to make use of following API method:

    GET /reports/getSharePointSiteUsageDetail(period='{period_value}’)
    GET /reports/getSharePointSiteUsageDetail(date={date_value})

    This API requires following permissions. We will consider them in this article. I want to analyze the sharepoint usage and want to update it to a list afterwards, that’s why I will make use of Application – Reports.Read.All

    Permission typePermissions (from least to most privileged)
    Delegated (work or school account)Reports.Read.All
    Delegated (personal Microsoft account)Not supported.
    ApplicationReports.Read.All
    SharePointSiteUsage Method Screenshot Graph API
    reportRoot: getSharePointSiteUsageDetail – Microsoft Graph v1.0 | Microsoft Docs

    Register the Enterprise Application

    After we figured out what permissions we need, we register the app.

    Prerequisites

    You have to have the role ‘Global Administrator’ to grant the permissions for an Enterprise Application.

    Registration

    Visit the Azure Portal URL and switdh to the app registrations sites. Directlink: App registrations – Microsoft Azure

    Click on new registration

    Register New App for Graph Api

    Give your application a name, click on Accounts in this organizational directory only, select mobile as platform, after that click on register.

    Application Registration for Graph Api

    Take a note of the Application (client) ID, you will need it to authenticate against the Graph API.

    Enterprise Application

    Grant API Permissions for App Registration

    After creating the app, we have to give it the permissions, which we have defined in the first step.

    Enterprise Application API permission

    Click on Microsoft Graph.

    Graph API screenshot

    Grant it Application permissions

    Application Permissions

    Now you have to select the permissions, for which you want to use the Graph API. I just need the information for Reports.Read.All. If you don’t know which permission to take, check the considerations part of this post.

    reports.read.all permission for Graph API

    As you can see, the permission is not granted for this tenant.

    not granted permissions screenshot for Graph API

    Create Client Secret for App Registration

    In order to authenticate to the Graph API in PowerShell, you have to create a client secret.

    Click on Certificates & secrets and then on New client secret

    Create client secret for Graph API enterprise application

    Set a Description and define when it will be expiring. I would recommend to give it a description, which you can recognize, for what it will be used in future. I have set 24 months, because I want to make use it in an automation, which should run for a long term. When finished, click Add.

    Usage Scripts client secret for Graph API

    Take Note of the value! You wont see it again, if you leave the site.

    Client Secret for Graph API obfuscated

    Consent the Requested permissions for App Registration

    Caution: You have to consent the created application with the global administrator role.

    https://login.microsoftonline.com/TENANTDomain/adminconsent?client_id=CLIENTID
    

    The URL for my dev tenant is like:

    https://login.microsoftonline.com/devmodernworkplace.onmicrosoft.com/adminconsent?client_id=949710fd-8d80-48ee-8c1b-a6f5e9e32be3

    Choose an account with global administrator role.

    Global administrator account login to grant permission for Graph API

    As you can see the permissions, which we have configured, are showing up:

    permission grant for created app for Graph API

    Since you have not set a redirect url, you will encounter this issue, which you can ignore.

    this ocurs, since we have not configured a redirect url

    Check Permission consent

    You can check that the permission is granted, if you see the green check marks.

    granted permission for enterprise application for Graph API

    Script To Acess SharePoint via the Graph API (PowerShell)

    The script contains two parts. The first part is about authentication and the second is about getting the data provided.

    Authentication

    I am making use of a credential export to be sure, that nobody steals the credentials, when it is in plain text. If you don’t know how to, check out: Use credentials in PowerShell – SPO Scripts

    Function Export-CredentialFile 
    {
        param(
        [Parameter(Mandatory=$true,Position=0)]
        $Username,
        [Parameter(Mandatory=$true,Position=1)] 
        $Path
        )
        
        
        While ($Path -eq "")
        {
            $Path = Read-Host "The path does not exist. Where should the credentials be exported to?"
        }
        $ParentPath = Split-Path $Path
        If ((Test-Path $ParentPath) -eq $false)
        {
            New-Item -ItemType Directory -Path $ParentPath
        }
        $Credential = Get-Credential($Username)
        $Credential | Export-Clixml -Path $Path
        Return $Credential
    }
    Function Import-CredentialFile ($Path)
    {
        if (! (Test-Path $Path))
        {
            Write-Host "Could not find the credential object at $Path. Please export your credentials first"
            Export-CredentialFile
        }
        Import-Clixml -Path $Path
    }
    $AppId = '949710fd-8d80-48ee-8c1b-a6f5e9e32be3'
    $CredentialPath = "C:\temp\$AppId.key"
    Export-CredentialFile -Username $AppId -Path $CredentialPath

    After doing this, we notice, that the file with the app id as name, has an encrypted password. So we splitted credentials from script to increase the security. This credential file can only be used on the machine and with the user, who has created it.

    PowerShell credential object

    If we run follwing script afterwards, we will notice, that the $AuthorizationRequest will show us a token with an bearer token.

    $AppId = '949710fd-8d80-48ee-8c1b-a6f5e9e32be3'
    $CredentialPath = "C:\temp\$AppId.key"
    $AppCredential = Import-CredentialFile -Path $CredentialPath
    
    $Scope = "https://graph.microsoft.com/.default"
    $Url = "https://login.microsoftonline.com/devmodernworkplace.onmicrosoft.com/oauth2/v2.0/token"
    
    $Body = @{
        client_id = $AppCredential.UserName
        client_secret = $AppCredential.GetNetworkCredential().password
        scope = $Scope
        grant_type = 'client_credentials'
    }
    
    $AuthorizationRequest = Invoke-RestMethod -Uri $Url -Method 'post' -Body $Body
    $AuthorizationRequest
    
    answer to the authorization request

    Access SharePoint Online with Authorization Token

    Now that we got the access token, we can connect to SharePoint Online with following script. You can use the uris (methods), defined in Microsoft docs.

    $Uri = "YOURURI"
    
    $Header = @{Authorization = "$($AuthorizationRequest.token_type) $($AuthorizationRequest.access_token)"}
    $SitesRequest = Invoke-RestMethod -Uri $Uri -Method 'Get'  -Headers $Header

    Get Site Usage Details

    You can get the site usage with following uri “https://graph.microsoft.com/beta/reports/getSharePointSiteUsageDetail(period='{D90}’)?`$format=application/json”. The number next to the D means the amount of days. So for my example it shows the usage of all sites for the last 90 days. You can replace D90 with D7, D30, and D180.

    With this script you can get the site usage for the last 90 days:

    $Uri = "https://graph.microsoft.com/beta/reports/getSharePointSiteUsageDetail(period='{D90}')?`$format=application/json"
    
    $Header = @{Authorization = "$($AuthorizationRequest.token_type) $($AuthorizationRequest.access_token)"}
    $SitesRequest = Invoke-RestMethod -Uri $Uri -Method 'get'  -Headers $Header 
    
    $Sites.value | Out-GridView -PassThru

    Bonus: Ready-to-Use Script

    If you want to make use of the script, you have to change the parameters $GraphUrl and $AppID.

    Param(
        $AppId = '949710fd-8d80-48ee-8c1b-a6f5e9e32be3',
        $GraphUrl = "https://login.microsoftonline.com/devmodernworkplace.onmicrosoft.com/oauth2/v2.0/token",
        $Scope = "https://graph.microsoft.com/.default",
        $Uri = "https://graph.microsoft.com/beta/reports/getOffice365GroupsActivityDetail`(`period=`'`D90`'`)?`$format=application/json",
    )
    
    Function Export-CredentialFile 
    {
        param(
        [Parameter(Mandatory=$true,Position=0)]
        $Username,
        [Parameter(Mandatory=$true,Position=1)] 
        $Path
        )
        
        
        While ($Path -eq "")
        {
            $Path = Read-Host "The path does not exist. Where should the credentials be exported to?"
        }
        $ParentPath = Split-Path $Path
        If ((Test-Path $ParentPath) -eq $false)
        {
            New-Item -ItemType Directory -Path $ParentPath
        }
        $Credential = Get-Credential($Username)
        $Credential | Export-Clixml -Path $Path
        Return $Credential
    }
    Function Import-CredentialFile ($Path)
    {
        if (! (Test-Path $Path))
        {
            Write-Host "Could not find the credential object at $Path. Please export your credentials first"
            Export-CredentialFile
        }
        Import-Clixml -Path $Path
    }
    
    $CredentialPath = "C:\temp\$AppId.key"
    Export-CredentialFile -Username $AppId -Path $CredentialPath
    
    $AppCredential = Import-CredentialFile -Path $CredentialPath
    
    $Body = @{
        client_id = $AppCredential.UserName
        client_secret = $AppCredential.GetNetworkCredential().password
        scope = $Scope
        grant_type = 'client_credentials'
    }
    
    $AuthorizationRequest = Invoke-RestMethod -Uri $GraphUrl -Method 'post' -Body $Body
    
    $Header = @{Authorization = "$($AuthorizationRequest.token_type) $($AuthorizationRequest.access_token)"}
    $SitesRequest = Invoke-RestMethod -Uri $Uri -Method 'get'  -Headers $Header 
    
    $SitesRequest.value | Out-GridView -PassThru

    Conclusio

    In this article you saw how to find the right permission for the enterprise application, which you need to access the SharePoint via the Graph API with PowerShell. After doing this, you can authenticate and analyze the data.

    Further Docs

    reportRoot: getSharePointSiteUsageDetail – Microsoft Graph v1.0 | Microsoft Docs

  • Add-PNPField: Add SharePoint columns with PowerShell

    Add-PNPField: Add SharePoint columns with PowerShell

    Sometimes it’s hard enough to map the exact business requirements on the intranet. Implementing them manually afterwards can lead to the requirements not being implemented exactly in the further course. Also doing it manually is very time consuming and frustrating, when mistakes are made. This is where automation comes in handy. If you are looking forward to automate your intranet, it is crucial to design lists by adding them programtically. When you add SharePoint columns with PowerShell, you can be sure, that mistakes are not made like choosing the wrong column type or wrong internal name. You basically ensure that your intranet follows the standards, which have been defined in the beginning by your stakeholders. Thus I thought it might be interesting to share my experiences with you. All my descriptions have been tested and can be used on lists and libraries.

    Prerequisites

    You have to have access to the sites, where you want to add the SharePoint columns with PowerShell. Check out the article of Microsoft, if you are not sure, how to customize the permissions: Customize permissions for a SharePoint list or library – SharePoint (microsoft.com).

    To have a understanding, how lists / libraries can be designed, will help you to automate the procedures. If you are not familiar with this, try out creating and defining a list manually, to know what options are available.

    Scenario

    Lets assume, we are looking forward, to create a list with various column types.

    List scenario

    The beginning will be this plank list:

    Plank list

    Description

    In the following, I will show you step by step how to add the SharePoint columns with PowerShell.


    Step 1 – Connect to SharePoint

    In order to add SharePoint Columns with PowerShell, you have to connect to SharePoint Online with PNP. PowerShell. If you are not sure about how to, check out: Connect to SharePoint Online with PowerShell (workplace-automation.com/)

    $Credential = Get-Credential
    Connect-PnPOnline -Url "https://devmodernworkplace.sharepoint.com/sites/sales" -Credential $Credential

    Step 2 – Get the List

    Get the SharePoint list or library with Get-PNPList

    $List = Get-PnPList -Identity "ID of the list"
    $List = Get-PnPList -Identity "978f0ca5-7cc9-4151-8ba0-2fc45d736723"

    You can find the internal name of the list by running Get-PNPList.

    Internal Name of Opportunities2

    Step 3 – Add SharePoint Columns

    After we have connected to the site and got the list/ library, we can add the desired fields. If you don’t want to add the fields to the default view, remove the paramter -AddToDefaultView.

    I have used $DisplayName also for the paremter InternalName, since we cannot ensure, that the name for the internal name will be set, as you can see in the screenshot:

    Screenshot of differing internal name

    Add String Column

    $DisplayName = "Delivery address (Customer)"
    
    Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type Text -AddToDefaultView

    The result of the string column looks like this:

    added string column

    Add Date Colum with Time

    If you want to add the date column, without time, you can do it like this:

    $DisplayName = "Date"
    Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type DateTime -AddToDefaultView

    The result of the date column with time looks like this:

    Result of Date column with time

    Add Date Column without Time

    If you want to add the date column without time, you can do it like this:

    $DisplayName = "Date without time"
    $PNPField = Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type DateTime -AddToDefaultView
    [XML]$SchemaXml = $PNPField.SchemaXml
    $SchemaXml.Field.SetAttribute("Format","DateOnly")
    
    Set-PnPField -List $List -Identity $PNPField.Id -Values @{SchemaXml =$SchemaXml.OuterXml} -UpdateExistingLists

    The result of the date column looks like this:

    Result of Date Column without TIme

    Add Lookup Column

    If you want to add lookup columns, you have to know the ID of the list and the Internal Name of the column, which you want to look up.

    Let’s assume, that we will lookup the email address from the list Contacts.

    Lookup column of contacts

    The ID of the list contact is: a66ff40f-ceca-4f6b-a523-7fe32a97ea11

    ID of contacts list

    For the email column, you other can do it with GUI: Determine internal name of SharePoint Columns with GUI – SPO Scripts or with PowerShell:

    Get-PnPField -List $List
    
    #Or
    
    Get-PNPField -List "INTERNALNAME OFLIST"
    Lookup Field of Email

    Now you can add the column email address to the opportunity list.

    $DisplayName = "Email"
    $PNPField = Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type Lookup  -AddToDefaultView
    Set-PnPField -List $List -Identity $PNPField.Id -Values @{LookupList= "a66ff40f-ceca-4f6b-a523-7fe32a97ea11"; LookupField="Email"}

    The result of the lookup column looks like this:

    Result of lookup field

    Add Choice Column

    First you define the choices, than you add the column.

    $Choices = (
    "Choice 1",
    "Choice 2",
    "Choice 3"
    )
    
    $DisplayName = "Choice"
    $PNPField = Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type Choice -AddToDefaultView -Choices $Choices

    Result:

    Result of choice column

    Add Boolean Column

    $DisplayName = "Win"
    $PNPField = Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type Boolean -AddToDefaultView

    The result of the boolean column looks like this:

    Result of boolean value

    Add Number Value

    $DisplayName = "Number of Stakeholder"
    $PNPField = Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type Number -AddToDefaultView

    The result of the number column looks like this:

    Result of number column

    Add Currency Column

    $DisplayName = "Deal Size"
    $PNPField = Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type Currency -AddToDefaultView

    The result of the currency column looks like this:

    Result of currency column

    Add Notes Column

    $DisplayName = "Notes"
    $PNPField = Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type Note -AddToDefaultView

    Result:

    Result of note columns

    Add Location Column

    $DisplayName = "Location"
    $PNPField = Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type Location -AddToDefaultView

    The result of the location column looks like this:

    Result of location column

    Add Person / User Column (single person)

    $DisplayName = "Sales Manager"
    $PNPField = Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type User -AddToDefaultView

    The result of the single people column looks like this:

    Result of Person Column

    Add Person / User Column (multiple persons)

    $DisplayName = "MultiPerson"
    $PNPField = Add-PnPField -List $List -InternalName $DisplayName -DisplayName $DisplayName -Type User -AddToDefaultView 
    [XML]$SchemaXml = $PNPField.SchemaXml
    
    $SchemaXml.Field.SetAttribute("Mult","TRUE")
    $OuterXML = $SchemaXml.OuterXml.Replace('Field Type="User"','Field Type="UserMulti"')
    Set-PnPField -List $List -Identity $PNPField.Id -Values @{SchemaXml =$OuterXML} -UpdateExistingLists
    

    The result of the multi people column looks like this:

    multi user column

    Add Hyperlink Column

    $DisplayName = "Url to Invoice"
    $PNPField = Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type  -AddToDefaultView

    The result of the hyperlink column looks like this:

    Result of URL Column

    Add Managed Metadata Column

    Retrieve the Unique Identifier of the term set, which you want to add as a metadata column.

    Visit admin center url and follow the numbers in the screenshot:

    https://tenant-admin.sharepoint.com

    TaxonomyItemID

    Now add it to TaxononmyItemID and run the cmdlets

    $DisplayName = "Departments"
    Add-PnPTaxonomyField -List $List -DisplayName $DisplayName -InternalName $DisplayName -TaxonomyItemId f076462d-bde7-4fa4-aa7b-4409a769fcd1

    The result is, that the department column, pops up as a managed metadata column.

    result of managed metadata column

    Add Calculated Column

    You can add a calculated column by defining a formular per each column. Check the reference, which shows you, how to define a formular: Examples of common formulas in lists – SharePoint (microsoft.com)

    Note: When you define the formular, you have to take the displayname. Internal names do not work currently.

    Otherwise, you see this error:

    Error when using internal name of column
    $DisplayName = "Revenue per stakeholder"
    $PNPField = Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type Calculated -AddToDefaultView -Formula ="[Deal Size]/[Number of Stakeholder]"
    

    The result of the calculated column looks like this:

    Result of calculated column

    Add Image Column

    $DisplayName = "Logo of customer"
    $PNPField = Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type Thumbnail -AddToDefaultView

    The result of the image column, looks like this:

    Result of image column

    Add Task Outcome Column

    The task outcome column is designed to define the outcome of tasks like approvals. So basically it is a choice column, but you cannot allow fill-in choices and it is a single selection choice.

    I will define following choices:

    approved
    rejected
    to be reviewed by supervisor

    The default is empty. I want the people to make conscious decisions.

    [array]$Choices = (
    "approved",
    "rejected",
    "to be reviewed by supervisor"
    )
    $DefaultChoice = ""
    
    $Choices = $Choices |ConvertTo-Xml -NoTypeInformation
    $Choices = $Choices.Objects.OuterXml
    $Choices =  $Choices -replace "Objects", "CHOICES"
    $Choices =  $Choices -replace "Object", "CHOICE"
    
    
    
    $FieldXML = @"
    <Field RowOrdinal="0" ColName="nvarchar15" Name="$DisplayName" StaticName="$DisplayName" SourceID="{$($List.Id)}" ID="{$([guid]::NewGuid())}" Indexed="FALSE" Viewable="TRUE" EnforceUniqueValues="FALSE" Required="FALSE" DisplayName="$DisplayName" Type="OutcomeChoice">
        <Default>$DefaultChoice</Default>
        $($Choices)
    </Field>
    "@
    $PNPField = Add-PnPFieldFromXml -List $List -FieldXml $FieldXML 

    If you want to make the field visible in the default view, you have to run following cmdlets:

    $PNPView = Get-PnPView -List $List | Where-Object {$_.DefaultView -eq $true}
    $PNPView.ViewFields.Add($PNPField.InternalName)
    $PNPView.Update()

    The result of our task outcome column looks like this:

    result of task outcome column

    Bonus: Complete Script

    Like everytime, I have provided you the whole script to add a column:

    $Credential = Get-Credential
    Connect-PnPOnline -Url "https://devmodernworkplace.sharepoint.com/sites/sales" -Credential $Credential
    $List = Get-PnPList -Identity 978f0ca5-7cc9-4151-8ba0-2fc45d736723
    
    
    $DisplayName = "Departments"
    $PNPField = Add-PnPField -List $List -DisplayName $DisplayName -InternalName $DisplayName -Type  -AddToDefaultView

    Conclusio

    As you can see, adding SharePoint Columns with PowerShell can save you a lot time, especially if you have to do it on multiple lists/ libraries and sites. I hope to save you a ton of work with this cheat sheet.

    References

    GitHub – pnp/powershell: PnP PowerShell

    FieldCollection.AddFieldAsXml-Methode (Microsoft.SharePoint.Client) | Microsoft Docs

    Image by StartupStockPhotos from Pixabay

  • Use credentials in PowerShell

    Use credentials in PowerShell

    Credentials are necessary, if you want to access systems or APIs. Credentials can be used interactively and within a script. If you want to use credentials in PowerShell to automate processes, you might have to export your credentials and import them within your automation solution.

    In this article I will show you a few ways to make use of credentials, how to export and import credential objects and security considerations.

    Functionality

    You export the credentials with PowerShell the context the user whith which you are running the cmdlets, also the machine will be considered. So if you copy the credential file to another machine, it won’t work with the same user and you have to re-export the credential file.

    Security considerations

    Every initialization of credentials (interactive or automated) will lead to a prompt like this. When you export credentials with PowerShell consider following advice:

    Be sure, that you do this close to your authentication and do not enter your credentials on client/ servers, which are not trusted.

    Credential Prompt

    Why?

    You might think this is safe. If we try to read the password, it looks like this:

    Credential Secure String

    But there is still a way to read the password:

    Credential Clear String

    The risk will be there, when you enter your credentials to a shell, which you are not owner of.

    • Recommendation
      1. If you have to enter it in a Shell, which you do not controll, ensure, that the credential object will be initialized like this $Credential = $null
      2. If possible, try to run your scripts within windows authentication. In PNP.PowerShell it looks like this:
    Connect-PNPOnline -Url $Url -UseCurrentCredentials

    Using credentials interactively

    This will lead to a prompt.

    Credential Prompt
    $UserName = "John.Doe@Contoso.com"
    $Credential = Get-Credential ($UPN)

    You can than make use of the credentials like this:

    Connect-PnPOnline -Url "https://devmodernworkplace.sharepoint.com/sites/sales" -Credentials $Credential

    Using credentials non-interactively

    For this use case I wrote two ready-to-use functions.

    Function Export-CredentialFile 
    {
        param(
        [Parameter(Mandatory=$true,Position=0)]
        $Username,
        [Parameter(Mandatory=$true,Position=1)] 
        $Path
        )
        
        
        While ($Path -eq "")
        {
            $Path = Read-Host "The path does not exist. Where should the credentials be exported to?"
        }
    
        $ParentPath = Split-Path $Path
        If ((Test-Path $ParentPath) -eq $false)
        {
            New-Item -ItemType Directory -Path $ParentPath
        }
    
        $Credential = Get-Credential($Username)
        $Credential | Export-Clixml -Path $Path
        Return $Credential
    }
    
    Function Import-CredentialFile ($Path)
    {
        if (! (Test-Path $Path))
        {
            Write-Host "Could not find the credential object at $Path. Please export your credentials first"
            Export-CredentialFile
        }
        Import-Clixml -Path $Path
    }
    

    You can call the functions like this:

    $Path = "C:\keys\serkar.key"
    $UserName = "Serkar@devmodernworkplace.onmicrosoft.com"
    
    Export-CredentialFile -Username $UserName -Path $Path
    $Credential = Import-CredentialFile -Path $Path

    And afterwards you can connect to SharePoint. If you feel unsure about it, check out this post: Connect to SharePoint Online with PowerShell (workplace-automation.com/)

    Connect-PnPOnline -Url https://devmodernworkplace.sharepoint.com/sites/sales -Credentials $Credential

    But be sure to initialize the $Credential afterwards, so the potential security vector is as small as possible when you export credentials with PowerShell.

    Additional links

    Get-Credential (Microsoft.PowerShell.Security) – PowerShell | Microsoft Docs

  • How to use LINQ in PowerShell to compare arrays

    How to use LINQ in PowerShell to compare arrays

    LINQ stands for Language Integrated Query and if you use LINQ in PowerShell it might boost the performance of your scripts. In this article, I’ll show you how to use LINQ in PowerShell for comparing arrays.

    In this article, I will show you except and intersect of LINQ. There are many more methods. If you are interested in other methods, check out the official documentation of Microsoft.

    Concept of using LINQ in PowerShell

    We have three amounts red, orange and yellow. The aim is to only get a specific amount e.g. only the red portion.

    Amounts with strings
    $Red = @( "A", "B", "C")
    $Yellow = @("C","D","E")

    We also can do this for integer values.

    amounts with integers

    $Red = @( 1..5)
    $Yellow = @(4..10)

    Using LINQ for Strings in arrays

    I will show you how to use LINQ for string in this chapter. We will cover the methods except and intersect.

    String – Get the red amount

    In order to get the red amount, you have to do following:

    $Red = @( "A", "B", "C")
    $Yellow = @("C","D","E")
    
    $Left  = [string[]]$Red
    $Right = [string[]]$Yellow
    
    [string[]][Linq.Enumerable]::Except($Left, $Right)

    By doing this, you’ll get only A and B:

    LINQ get the red amount in PowerShell

    String – Get the yellow amount

    In order to get the red amount, you have to change Right and Left in the LINQ cmdlet:

    $Red = @( "A", "B", "C")
    $Yellow = @("C","D","E")
    
    $Left  = [string[]]$Red
    $Right = [string[]]$Yellow
    
    [string[]][Linq.Enumerable]::Except($Right, $Left)

    By doing this, you’ll get only D and E:

    LINQ get the yellow amount in PowerShell

    String – Get the orange amount

    If we want to get the orange amount, we have to make use of intersect

    $Red = @( "A", "B", "C")
    $Yellow = @("C","D","E")
    
    $Left  = [string[]]$Red
    $Right = [string[]]$Yellow
    
    [string[]][Linq.Enumerable]::Intersect($Left, $Right)
    LINQ get the orange amount in PowerShell

    String – Get everything except orange

    $Red = @( "A", "B", "C")
    $Yellow = @("C","D","E")
    
    $Left  = [string[]]$Red
    $Right = [string[]]$Yellow
    
    [string[]]([Linq.Enumerable]::Except($Left, $Right) + [Linq.Enumerable]::Except($Right, $Left))

    By doing this, you’ll everything but not C:

    linq - get everything except the orange amount powershell

    Using LINQ for integers in arrays

    I will show you how to use LINQ for integers in this chapter. We will cover the methods except and intersect.

    Integer – Get the red amount

    In order to get the red amount, you have to do following:

    $Red = @( 1..5)
    $Yellow = @(4..10)
    
    $Left  = [int[]]$Red
    $Right = [int[]]$Yellow
    
    [int[]][Linq.Enumerable]::Except($Left, $Right)

    By doing this, you’ll get only 1, 2, 3:

    LINQ get only the red amount in PowerShell

    Integer – Get the yellow amount

    In order to get the red amount, you have to change Right and Left in the LINQ cmdlet:

    $Red = @( 1..5)
    $Yellow = @(4..10)
    
    $Left  = [int[]]$Red
    $Right = [int[]]$Yellow
    
    [int[]][Linq.Enumerable]::Except($Right, $Left)

    By doing this, you’ll get only 6, 7, 8, 9, 10:

    LINQ get the yellow amount in PowerShell for integers

    Integer – Get the orange amount

    If we want to get the orange amount, we have to make use of intersect

    $Red = @( 1..5)
    $Yellow = @(4..10)
    
    $Left  = [int[]]$Red
    $Right = [int[]]$Yellow
    
    [int[]][Linq.Enumerable]::Intersect($Right, $Left)
    LINQ get the orange amount in PowerShell for integers

    Integer – Get everything except orange

    $Red = @( 1..5)
    $Yellow = @(4..10)
    
    $Left  = [int[]]$Red
    $Right = [int[]]$Yellow
    
    [int[]]([Linq.Enumerable]::Except($Left, $Right) + [Linq.Enumerable]::Except($Right, $Left))

    By doing this, you’ll everything but not 5:

    LINQ get the everything but not the orange amount in PowerShell for integers
    Read more