Using Microsoft Graph SDK Cmdlets to Create a SharePoint Online List

Easier to Create SharePoint Lists with PnP.PowerShell

Last week, I wrote about how to use cmdlets from the PnP.PowerShell module to create and populate a list in a SharePoint Online site using data generated by the Teams Directory script. As benefits a module deeply rooted in SharePoint history, the cmdlets worked well and the script wasn’t too difficult to write.

The Microsoft Graph is supposed to be “the gateway to data and intelligence in Microsoft 365. It provides a unified programmability model that you can use to access the tremendous amount of data in Microsoft 365…” I don’t have much argument with this assertion because in most cases, it’s true. That is, until you come to SharePoint Online where the coverage of SharePoint objects and data is not as good as for other workloads.

This article describes some of the challenges involved in writing a script based on Microsoft Graph PowerShell SDK cmdlets and Graph API requests to create SharePoint lists similar to what I did using PnP.PowerShell. Let’s see how I got on.

How to Create SharePoint List Script in a Nutshell

The script is simple in concept. The data comes from a CSV file generated by the Teams Directory script. The script creates a list in a SharePoint Online site and populates the list with items imported from the CSV file. The plan was to use cmdlets from the Microsoft Graph PowerShell SDK (V2.8) because there appears to be cmdlets available for everything the script needs to do.

Connecting to the Graph and the Target Site

The first steps connect to the Graph with the relevant permissions and retrieve details of the site holding the list. The script then checks if the list already exists and if found, removes the list. Rebuilding a list from scratch is easier than attempting to synchronize changes.

Connect-MgGraph -Scopes Sites.ReadWrite.All, Sites.Manage.All -NoWelcome
$ListName = "Teams Directory - Graph"
# Get target site 
Write-Host "Fetching details of the target site and list..."
$Site =  Get-MgSite -Search 'Office 365 for IT Pros Communications'
# Get List
$List = Get-MgSiteList -SiteId $Site.Id -Filter "displayName eq 'Teams Directory - Graph'"
If ($List) {
    # Delete the list
    Write-Host ("Removing previous version of list {0}" -f $List.DisplayName)
    Remove-MgSiteList -SiteId $Site.Id -ListId $List.Id

Create SharePoint List with New-MgSiteList

The next step creates the list. The Graph SDK includes the New-MgSiteList cmdlet, but no matter what I did with the cmdlet, it refused to co-operate. Even the example from the Microsoft documentation failed with the following error:

New-MgSiteList_Create: Unable to determine type of provided column definition
Status: 400 (BadRequest)
ErrorCode: invalidRequest
Date: 2023-10-20T16:44:06

The workaround is to define the settings for the list and the columns in the list in a variable and use the variable as a parameter for a POST request to the site:

Write-Host "Defining the new list"
$Uri = ("{0}/Lists" -f $Site.Id)
$ListDetails = '{
    "displayName": "Teams Directory - Graph",
    "description": "Discover teams to join in Office 365 for IT Pros",
    "columns": [
        "name": "Deeplink",
        "description": "Link to access the team",
        "text": { }
        "name": "Description",
        "description": "Purpose of the team",
        "text": { }
        "name": "Owner",
        "description": "Team owner",
        "text": { }
        "name": "OwnerSMTP",
        "description": "Primary SMTP address for owner",
        "text": { }
        "name": "Members",
        "description": "Number of tenant menbers",
        "number": { }
        "name": "ExternalGuests",
        "description": "Number of external guest menbers",
        "number": { }
        "name": "Access",
        "description": "Public or Private access",
        "text": { }
Invoke-MgGraphRequest -Uri $Uri -Method POST -Body $ListDetails | Out-Null

Unlike the previous example, I chose to create a blank list. The list comes with a single field called Title, which the script renamed to TeamName. The internal name of the field remains Title, which is important to remember when updating records.

$List = Get-MgSiteList -SiteId $Site.Id -Filter "displayName eq 'Teams Directory - Graph'"
$ColumnId = (Get-MgSiteListColumn -SiteId  $Site.Id -ListId $List.Id | `
    Where-Object {$_.Name -eq 'Title'}).Id
Update-MgSiteListColumn -ColumnDefinitionId $ColumnId -SiteId $Site.Id -ListId $List.Id `
  -Description 'Name of the team' -DisplayName 'Team Name' -Name 'TeamName' | Out-Null

Adding Records to the List

After preparing the list, the script populates it with data imported from the Teams Directory. I ran into issues with the New-MgSiteListItem cmdlet. This could be a documentation issue, but some internet forums (like this example) indicate that this cmdlet has not had a happy history. I ended up creating each item as a custom object, wrapping the item data inside another custom object, converting it to JSON, and using the JSON content as a payload to post to the items endpoint:

$Uri = ("{0}/lists/{1}/items" -f $Site.Id, $List.Id)
ForEach ($Team in $TeamsData) {
  Write-Host ("Adding directory record for team {0} {1}/{2}" -f $Team.Team, $i, $TeamsData.Count)
  $FieldsDataObject  = [PSCustomObject] @{
        Title          = $Team.Team
        Deeplink       = $Team.Deeplink
        Description    = $Team.Description
        Owner          = $Team.Owner
        OwnerSMTP      = $Team.OwnerSMTP
        Members        = $Team.Members
        ExternalGuests = $Team.ExternalGuests
        Access         = $Team.Access
  $NewItem = [PSCustomObject] @{
        fields         = $FieldsDataObject
  $NewItem = $NewItem | ConvertTo-Json
  $Status = Invoke-MgGraphRequest -Method POST -Uri $Uri -Body $NewItem
  If ($Status.Id) {
     Write-Host ("Record added to list with id {0}" -f $Status.Id)

This approach works, but I could never write to a hyperlink field (something that the Add-PnPListItem cmdlet can do). Apparently, the Graph doesn’t currently support list hyperlink fields, so I ended up writing the deeplink to a team to a text field. The result is the list shown in Figure 1 where users see deeplinks that are not clickable. Users can copy the link to a browser tab and navigate to Teams that way, but that’s not very user-friendly. For small lists, you can create a hyperlink field in the list and copy deeplinks to that field. Users can then click on the link in the hyperlink field. Such a solution is unacceptable at any scale.

Teams directory data written to a SharePoint list using the Graph

Create sharepoint list
Figure 1: Teams directory data written to a SharePoint list using the Graph

You can download the full script from GitHub.

Choose PnP.PowerShell to Create SharePoint Lists

What I learned from the exercise is that the PnP.PowerShell module is a more robust and reliable tool to use when working with SharePoint Online lists. PnP has its own quirks, but it works. I spent far too long chasing Graph SDK cmdlets that didn’t work as documented or couldn’t do what I wanted, so I recommend that you use PnP until Microsoft sorts out the SDK cmdlets and documentation.

In closing, I asked Bing Chat Enterprise to write a script to create and populate a list in a SharePoint site Online based on the Microsoft Graph PowerShell SDK. The results were impressive (Figure 2).

Bing Chat Enterprise script to create and populate a SharePoint Online list
Figure 2: Bing Chat Enterprise script to create and populate a SharePoint Online list

After this experience, I might use Bing Chat Enterprise more often in the future to sketch out the basics of scripts. In this case, Bing Chat Enterprise was helpful. In others, it’s been awful. But that’s the nature of generative AI in respect of its ability to regurgitate errors couched in what seems to be impressive terms.

Keep up to date with developments like how to create SharePoint lists with the Microsoft Graph PowerShell SDK by subscribing to the Office 365 for IT Pros eBook. Our monthly updates make sure that our subscribers understand the most important changes happening across Office 365.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.