Find a SharePoint List’s Internal Column Names with PowerShell

You write a script against a SharePoint list, ask for a column by the name you can see in the browser, and get nothing back. No error, just an empty value.

The column is called Project Status on screen. It is not called that underneath.

Run against a real list on PnP.PowerShell 2.12.0.

Every column has two names

The Title is the label in the browser. Anyone can change it and it can contain anything.

The InternalName is the key. It is set once, when the column is created, and it never changes again.

Here is the difference in one line:

$item = Get-PnPListItem -List 'Projects' -Id 1

$item['ProjectStatus']    # internal name
$item['Project Status']   # display name
By internal name 'ProjectStatus' : 'Complete'
By display name 'Project Status' : ''

An empty string, not an error. That is why this is so easy to lose an hour to – the script runs, it just quietly does nothing.

Getting the list

The obvious command gives you far more than you want:

Get-PnPField -List 'Projects'

On a list where I had created exactly five columns:

Total fields          : 90
Hidden                : 61
Inherited from base   : 83
Actually yours        : 5

Ninety. So filter it down to the ones you actually made:

Get-PnPField -List 'Projects' |
    Where-Object { -not $_.FromBaseType -and -not $_.Hidden } |
    Select-Object Title, InternalName, TypeAsString |
    Format-Table -AutoSize
Title          InternalName  TypeAsString
-----          ------------  ------------
Project Status ProjectStatus Choice
Project Owner  ProjectOwner  User
Due Date       DueDate       DateTime
Budget         Budget        Currency
Notes          Notes         Note

FromBaseType is the useful one. It is true for everything SharePoint gave you and false for everything you added.

What happens to spaces

Those internal names are clean because they were created by script. Columns created in the browser are not, because SharePoint derives the internal name from whatever you typed.

I made one with a space on purpose:

Add-PnPField -List 'Projects' -DisplayName 'Demo Review Date' `
             -InternalName 'Demo Review Date' -Type DateTime
Title        : Demo Review Date
InternalName : Demo_x0020_Review_x0020_Date
StaticName   : Demo_x0020_Review_x0020_Date

Every space becomes _x0020_. A column called “Date of Last Review” ends up as Date_x0020_of_x0020_Last_x0020_Review, and that is what your script has to use.

This is also why you should create columns with a short name and rename them afterwards, which brings us to the thing that actually catches people.

Renaming does not change the internal name

I renamed that column and looked again:

Set-PnPField -List 'Projects' -Identity 'Demo Review Date' -Values @{ Title = 'Renamed Review Date' }
Title        : Renamed Review Date
InternalName : Demo_x0020_Review_x0020_Date

The label changed. The key did not, and it never will.

So a column that says Review Date today might be Temp_x0020_Column underneath because of what somebody typed two years ago. You cannot work out the internal name by looking at the browser. You have to ask.

The practical version of that: create the column with the internal name you want, then rename it to whatever reads well. Give it ReviewDate first, then change the label to “Date of Last Review”. You get a clean key and a readable heading.

Built-in columns are worse

Some of the ones you use most have internal names with no relationship to the label at all:

Title        InternalName TypeAsString
-----        ------------ ------------
Attachments  Attachments  Attachments
Created By   Author       User
Content Type ContentType  Computed
Created      Created      DateTime
Modified By  Editor       User
Name         FileLeafRef  File
URL Path     FileRef      Lookup
ID           ID           Counter
Modified     Modified     DateTime
Title        Title        Text

Created By is Author. Modified By is Editor. Those two are the ones everybody hits, and there is nothing on screen that hints at it.

In a document library it gets stranger still:

Title        InternalName    TypeAsString
-----        ------------    ------------
Created By   Author          User
Type         DocIcon         Computed
Modified By  Editor          User
File Size    File_x0020_Size Lookup
Name         FileLeafRef     File
URL Path     FileRef         Lookup

A file’s Name is FileLeafRef. Its path is FileRef. And File Size carries the _x0020_ encoding as a built-in, which tells you Microsoft named it in a browser too.

One thing that will not work at all

I tried to make a column called Cost & Time-Frame to see what an ampersand does:

An error occurred while parsing EntityName. Line 1, position 32.

Not an encoded name – a failure. PnP builds field XML underneath, and a raw ampersand breaks the parse before SharePoint ever sees it. The error mentions XML parsing and does not mention your column, which is not helpful when you are three lines into a provisioning script.

Note that this is a script limitation. The browser will happily create that column and encode it for you. So this is one of the few cases where the UI does something PowerShell will not.

The line worth keeping

Get-PnPField -List 'YourList' |
    Where-Object { -not $_.FromBaseType -and -not $_.Hidden } |
    Select-Object Title, InternalName, TypeAsString |
    Sort-Object Title

Run it before you write anything against a list you did not create yourself. Thirty seconds, and it saves the empty-string hour 🙂