Lock SharePoint column, make field ReadOnly or ReadOnlyEnforced

Prevent SharePoint List filed editing from end user by making it read only


When a new site column is created, a SharePoint admin can set it as read only or as read only enforced to prevent the user of editing the data stored in the field. This can be done either through PowerShell or CSOM. You can see the onet.xml schema here and the description of the mentioned read only properties.


Lock item using SharePoint Framework ListView Command Set or Field Customizer


We can rely on modern SharePoint development using the SharePoint Framework to do that for us. SharePoint Framework Command Set like one created by @Alex Terentiev can be used to lock down a field. While the SharePoint Framework extensions are handy they cannot fully prevent the end user to change the value of field locked by front end code. Since the SharePoint Framework runs under the context of the user, users with higher permissions like site collection admin rights would still be able to bypass that restriction from the UI.


What if only system with SharePoint elevated permissions has to change a field value


There are cases where the field should be locked and editable only by a web service or job using CSOM or other server code that executes commands with elevated rights. Then we can set the field as read only or read only enforced with the help of PnP PowerShell.


A little experiment, read only field vs read only enforced


A quick and convenient way to change existing SharePoint site column attributes, lists or content types is to use PowerShell 5.0 and the PnP SharePoint PowerShell cmdlets. My experiment would include creation of a field, set it as read only and then set it as read only enforced. The initial step in the code below would include creation of field called 'Bio', content type and list from scratch.


Connect-PnPOnline -Url https://xxx.sharepoint.com/sites/velindev -Credentials pru

Add-PnPField -DisplayName 'Bio' -InternalName Bio -Type Text -Id 8aa642a5-9bd5-46e6-ac33-5de5d795741e -Group 'MyGroup'

Add-PnPContentType -Name 'Profile' -ContentTypeId 0x01001409de18cddb413ba33e41bedaa89134 -Group 'MyGroup'
$profile = Get-PnPContentType -Identity 'Profile'

Add-PnPFieldToContentType -Field Bio -ContentType $profile

New-PnPList -Title 'Profiles' -Template GenericList -Url Lists/Profiles
$profiles = Get-PnPList -Identity Lists/Profiles

Set-PnPList -Identity 'Profiles' -EnableContentTypes $true -EnableVersioning $true -MajorVersions 100

Add-PnPContentTypeToList -List $profiles -ContentType $profile -DefaultContentType

Add-PnPView -List $profiles -Title Bio -SetAsDefault -Fields Title,Bio

$bio = Get-PnPField -Identity 8aa642a5-9bd5-46e6-ac33-5de5d795741e


At that point of the provisioning, the 'Bio' site column is visible and editable in the list I created. Now let's make it read only and push the update to all the SharePoint lists containing the site column.


$bio.ReadOnlyField = $true

# Update and push changes to the list
$bio.UpdateAndPushChanges($true)
$profile.Context.ExecuteQuery();


After the script is run, the schema for the field looks like


$bio = Get-PnPField -Identity 8aa642a5-9bd5-46e6-ac33-5de5d795741e
$bio.SchemaXml
<Field Type="Text" Name="Bio" DisplayName="Bio" ID="{8aa642a5-9bd5-46e6-ac33-5de5d795741e}" Group="MyGroup" Required="TRUE" SourceID="{f7fb12c3-ca68-4060-b1b0-c27a6bfffeb2}" StaticName="Bio"
Version="4" AllowDeletion="TRUE" ReadOnly="TRUE" />


Quick look at the SharePoint list edit form shows that the 'Bio' field is not present anymore

SharePoint page opened in Browser

Quick look at the SharePoint list quick edit view shows that the 'Bio' field is displayed, but cannot be edited


SharePoint page opened in Browser

The field is not even visible in the content type therefore end user cannot reveal it by changing content type column settings


SharePoint page opened in Browser


It is also hidden in the SharePoint site column settings which makes it truly read only. It can still be listed programmatically if we get it with PowerShell Get-PnPField


Title InternalName Id
----- ------------ --
Bio Bio 8aa642a5-9bd5-46e6-ac33-5de5d795741e


Now, it is almost certain that can be modified by PowerShell, but how about front end code on behalf of the user such PnPJS. Let's test that using the SP Editor (Chrome extension).


import { default as pnp, ItemAddResult } from "pnp";

// add an item to the list
pnp.sp.web.lists.getByTitle("Profiles").items.add({
Title: "Title 123",
Bio: "Custom bio 123"
}).then((iar: ItemAddResult) => {
console.log(iar);
});


Ok, that did not seem to work when executed on behalf of user. A test with PnP PowerShell on the other hand worked.


$item = Get-PnPListItem -List Profiles -Id 2
$item['Bio'] = 'Custom Bio 1'
$item.Update()
$item.Context.ExecuteQuery()


Is the field ReadOnly attribute a good fit preventing user edit, but ok for code with elevated rights?


The above PowerShell code worked and it was able to change the field read only value. So the ReadOnly field is a good fit. If we want to update a field from elevated CSOM code we can. A user can read the field value changes in a list view, but not modify them.


About the behavior of the field ReadOnlyEnforced attribute


There is also field attribute called ReadOnlyEnforced, but not much information about it. Let's experiment and see what will happen. This attribute has to be changed through the xml schema since there isn't a CSOM property for that.


$bio = Get-PnPField -Identity 8aa642a5-9bd5-46e6-ac33-5de5d795741e
$bio.SchemaXml = '<Field Type="Text" Name="Bio" DisplayName="Bio" ID="{35140574-0c07-40f6-a277-5958565ff5ae}" Group="MyGroup" StaticName="Bio"
ReadOnlyEnforced="TRUE" />'

# Update and push changes to the list
$bio.UpdateAndPushChanges($true)
$profile.Context.ExecuteQuery()

# Had to disable the read only since we had that enabled
$bio = Get-PnPField -Identity 8aa642a5-9bd5-46e6-ac33-5de5d795741e
$bio.ReadOnlyField = $false
$bio.UpdateAndPushChanges($true)
$profile.Context.ExecuteQuery()

#To validate the schema is
$bio = Get-PnPField -Identity 8aa642a5-9bd5-46e6-ac33-5de5d795741e
$bio.SchemaXml
<Field Type="Text" Name="Bio" DisplayName="Bio" ID="{8aa642a5-9bd5-46e6-ac33-5de5d795741e}" Group="MyGroup" Required="TRUE" SourceID="{f7fb12c3-ca68-4060-b1b0-c27a6bfffeb2}" StaticName="Bio"
Version="16" AllowDeletion="TRUE" ReadOnlyEnforced="TRUE" ReadOnly="FALSE" />


How this affects the field now? I was able to add item manually as end user, also was able to add it with PnPJS and PnP PowerShell. Also the field is present in the edit form, settings content types and settings site columns sections. Now let's try to edit it with PnPJS.


import { default as pnp, ItemAddResult } from "pnp";

let list = pnp.sp.web.lists.getByTitle("Profiles");

list.items.getById(1).update({
Bio: "My New Bio"
}).then(i => {
console.log(i);
});


I was not able to update the field using pnp js, neither from the edit form. How about PnP PowerShell then?


$item = Get-PnPListItem -List Profiles -Id 2
$item['Bio'] = 'Custom Bio 17777'
$item.Update()
$item.Context.ExecuteQuery()


With field as ReadOnlyEnforced we can add new items with the field value filled-in, but not easily update it even with PowerShell


Didn't work either with PowerShell script, so seems that we can add item and have that field value filled-in, but it cannot easily be changed once created even with CSOM. I have not further tested that with Azure AD app principle instead of user admin account.


Conclusion


The point is clear, the ReadOnly and ReadOnlyEnforced SharePoint field attributes serve two different purposes. One behaves as end user read-only field able to change from system and the ReadOnlyEnforced behaves as add new value and forget about changing that. SharePoint site column with either of the attributes can still be shown in a SharePoint list view so the user can see its value, but not edit it.