Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 134 additions & 74 deletions scripts/spo-add-fields-to-contenttypes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,100 +94,159 @@ finally {
# [CLI for Microsoft 365](#tab/cli-m365-ps)

```powershell
# .\Add-FieldsToContentTypes.ps1 -SiteUrl "https://contoso.sharepoint.com/sites/CLIForM365" -ContentTypes "My Review CT","Global User Review CT" -WhatIf
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
param (
[Parameter(Mandatory = $true, HelpMessage = "Absolute URL of the site where the fields and content types exist.")]
[ValidatePattern('^https://')]
[string]$SiteUrl,

[Parameter(Mandatory = $true, HelpMessage = "Content type names to which the fields should be added.")]
[ValidateNotNullOrEmpty()]
[string[]]$ContentTypes,

[Parameter(HelpMessage = "Optional override for the field definitions.")]
[ValidateNotNull()]
[System.Collections.Hashtable[]]$FieldDefinitions
)

begin {
Write-Verbose "Ensuring CLI for Microsoft 365 session."
m365 login --ensure

if (-not $FieldDefinitions) {
$FieldDefinitions = @(
@{ FieldName = 'admin_review'; FieldId = '3eccccb1-9789-40e9-bee8-0e27a2b0ea9f'; FieldXml = "<Field Type='User' DisplayName='Admin Reviewer' Required='false' EnforceUniqueValues='false' Hidden='false' UserSelectionMode='PeopleAndGroups' Group='My Site Columns' ID='{3eccccb1-9789-40e9-bee8-0e27a2b0ea9f}' StaticName='admin_review' Name='admin_review' />" },
@{ FieldName = 'admin_reviewdate'; FieldId = 'bee79067-c92c-4d9c-b80c-63a75a468b16'; FieldXml = "<Field Type='DateTime' DisplayName='Admin Review Date' Required='false' EnforceUniqueValues='false' Hidden='false' Group='My Site Columns' ID='{bee79067-c92c-4d9c-b80c-63a75a468b16}' StaticName='admin_reviewdate' Name='admin_reviewdate' />" }
)
}

function CreateField {
param ([string] $FieldName, [string] $FieldXML)
$Script:Results = [System.Collections.Generic.List[pscustomobject]]::new()

try {
# Check if the field exists and create
$field = m365 spo field get --webUrl $siteUrl --title $FieldName | ConvertFrom-Json
function Ensure-SiteField {
param (
[Parameter(Mandatory = $true)] [string]$WebUrl,
[Parameter(Mandatory = $true)] [System.Collections.Hashtable]$Definition
)

if ($null -eq $field) {
# Add new field based on XML
m365 spo field add --webUrl $siteUrl --xml $FieldXML
Write-Host "Successfully created field '$FieldName'" -ForegroundColor Green
$fieldLookup = m365 spo field list --webUrl $WebUrl --output json | ConvertFrom-Json
$existingField = $fieldLookup | Where-Object { $_.Title -eq $Definition.FieldName -or $_.InternalName -eq $Definition.FieldName -or $_.Id -eq $Definition.FieldId }

if ($existingField) {
$Script:Results.Add([pscustomobject]@{
Action = 'Field'
Target = $Definition.FieldName
Status = 'Skipped'
Message = 'Field already exists'
})
return $existingField
}
else {
Write-Host "Field '$FieldName' already exists. Skipping creation" -f Green

if (-not $PSCmdlet.ShouldProcess($Definition.FieldName, 'Create site column')) {
$Script:Results.Add([pscustomobject]@{
Action = 'Field'
Target = $Definition.FieldName
Status = 'WhatIf'
Message = 'Field creation skipped'
})
return $null
}
}
catch {
Write-Error "Error in CreateField - $_"
throw
}
}

function CreateAndAddFieldsToCTs {
param ([string[]] $ContentTypes)
$createOutput = m365 spo field add --webUrl $WebUrl --xml $Definition.FieldXml --output json 2>&1
if ($LASTEXITCODE -ne 0) {
$Script:Results.Add([pscustomobject]@{
Action = 'Field'
Target = $Definition.FieldName
Status = 'Failed'
Message = $createOutput
})
return $null
}

try {
#region Create SharePoint Site Columns
$fieldsToCreate = @(
@{
FieldName = "admin_review"
FieldID = "3eccccb1-9789-40e9-bee8-0e27a2b0ea9f"
FieldXML = "<Field Type='User' DisplayName='Admin Reviewer' Required='false' EnforceUniqueValues='false' Hidden='false' UserSelectionMode='PeopleAndGroups' Group='My Site Columns' ID='{3eccccb1-9789-40e9-bee8-0e27a2b0ea9f}' StaticName='admin_review' Name='admin_review'></Field>"
},
@{
FieldName = "admin_reviewdate"
FieldID = "bee79067-c92c-4d9c-b80c-63a75a468b16"
FieldXML = "<Field Type='DateTime' DisplayName='Admin Review Date' Required='false' EnforceUniqueValues='false' Hidden='false' Group='My Site Columns' ID='{bee79067-c92c-4d9c-b80c-63a75a468b16}' StaticName='admin_reviewdate' Name='admin_reviewdate'></Field>"
}
$createdField = $createOutput | ConvertFrom-Json
$Script:Results.Add([pscustomobject]@{
Action = 'Field'
Target = $Definition.FieldName
Status = 'Created'
Message = 'Field created successfully'
})
return $createdField
}

function Add-FieldToContentType {
param (
[Parameter(Mandatory = $true)] [string]$WebUrl,
[Parameter(Mandatory = $true)] [string]$ContentTypeName,
[Parameter(Mandatory = $true)] [System.Collections.Hashtable]$Definition
)

foreach ($fieldToCreate in $fieldsToCreate) {
Write-Host "Creating the field - $($fieldToCreate.FieldName)" -ForegroundColor Yellow
CreateField -FieldName $($fieldToCreate.FieldName) -FieldXML $($fieldToCreate.FieldXML)
$contentTypeJson = m365 spo contenttype get --webUrl $WebUrl --name $ContentTypeName --output json 2>&1
if ($LASTEXITCODE -ne 0) {
$Script:Results.Add([pscustomobject]@{
Action = 'ContentType'
Target = $ContentTypeName
Status = 'Failed'
Message = $contentTypeJson
})
return
}
Write-Host `n `n
#endregion

#region Add Site Columns to Content Types
foreach ($contentType in $ContentTypes) {
$contentTypeObj = m365 spo contenttype get --webUrl $siteUrl --name $contentType | ConvertFrom-Json
if ($contentTypeObj.StringId) {
foreach ($fieldToCreate in $fieldsToCreate) {
Write-Host "Adding field - $($fieldToCreate.FieldName) to CT $contentType" -ForegroundColor Yellow

m365 spo contenttype field set --webUrl $siteUrl --contentTypeId $contentTypeObj.StringId --id $fieldToCreate.FieldID
Write-Host "Successfully added fields to CT $contentType" -ForegroundColor Green
Write-Host `n
}
}
else {
Write-Host "Content Type not found: $contentType" -ForegroundColor Green
Write-Host `n
}
$contentType = $contentTypeJson | ConvertFrom-Json
if (-not $contentType.StringId) {
$Script:Results.Add([pscustomobject]@{
Action = 'ContentType'
Target = $ContentTypeName
Status = 'Failed'
Message = 'Content type not found'
})
return
}

if (-not $PSCmdlet.ShouldProcess($ContentTypeName, "Add field $($Definition.FieldName)")) {
$Script:Results.Add([pscustomobject]@{
Action = 'ContentType'
Target = $ContentTypeName
Status = 'WhatIf'
Message = "Skipped adding field $($Definition.FieldName)"
})
return
}
#endregion
}
catch {
Write-Error "Error in CreateAndAddFieldsToCTs - $_"

$setOutput = m365 spo contenttype field set --webUrl $WebUrl --contentTypeId $contentType.StringId --id $Definition.FieldId --output json 2>&1
if ($LASTEXITCODE -ne 0) {
$Script:Results.Add([pscustomobject]@{
Action = 'ContentType'
Target = $ContentTypeName
Status = 'Failed'
Message = $setOutput
})
return
}

$Script:Results.Add([pscustomobject]@{
Action = 'ContentType'
Target = $ContentTypeName
Status = 'Updated'
Message = "Field $($Definition.FieldName) bound successfully"
})
}
}

try {
# SharePoint Online Site Url
$siteUrl = "https://contoso.sharepoint.com/sites/CLIForM365"

# Get Credentials to connect
$m365Status = m365 status
if ($m365Status -match "Logged Out") {
m365 login
process {
foreach ($definition in $FieldDefinitions) {
Ensure-SiteField -WebUrl $SiteUrl -Definition $definition
}

# Create and add fields to SharePoint content types
CreateAndAddFieldsToCTs -ContentTypes @("My Review CT", "Global User Review CT")
}
catch {
Write-Error "Something wrong: " $_
}
finally {
# Disconnect SharePoint online connection
m365 logout
foreach ($contentType in $ContentTypes) {
foreach ($definition in $FieldDefinitions) {
Add-FieldToContentType -WebUrl $SiteUrl -ContentTypeName $contentType -Definition $definition
}
}
}

end {
$Script:Results | Sort-Object Action, Target | Format-Table -AutoSize
}
```

[!INCLUDE [More about CLI for Microsoft 365](../../docfx/includes/MORE-CLIM365.md)]
Expand All @@ -200,6 +259,7 @@ finally {
| ------------------- |
| Ahamed Fazil Buhari |
| [Ganesh Sanap](https://ganeshsanapblogs.wordpress.com/about) |
| Adam Wójcik |

[!INCLUDE [DISCLAIMER](../../docfx/includes/DISCLAIMER.md)]
<img src="https://m365-visitor-stats.azurewebsites.net/script-samples/scripts/spo-add-fields-to-contenttypes" aria-hidden="true" />
22 changes: 14 additions & 8 deletions scripts/spo-add-fields-to-contenttypes/assets/sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,29 @@
"Script to create site columns and add them to content types"
],
"creationDateTime": "2023-09-06",
"updateDateTime": "2023-10-24",
"products": ["SharePoint"],
"updateDateTime": "2025-11-12",
"products": [
"SharePoint"
],
"metadata": [
{
"key": "PNP-POWERSHELL",
"value": "2.2.0"
},
{
"key": "CLI-FOR-MICROSOFT365",
"value": "7.0.0"
"value": "11.0.0"
}
],
"categories": ["Provision"],
"categories": [
"Provision"
],
"tags": [
"Connect-PnPOnline",
"Get-PnPField",
"Add-PnPFieldFromXml",
"Add-PnPFieldToContentType",
"m365 status",
"m365 login",
"m365 logout",
"m365 spo field get",
"m365 spo field list",
"m365 spo field add",
"m365 spo contenttype get",
"m365 spo contenttype field set"
Expand All @@ -55,6 +56,11 @@
"company": "",
"pictureUrl": "https://github.com/ahamedfazil.png",
"name": "Ahamed Fazil Buhari"
},
{
"gitHubAccount": "Adam-it",
"pictureUrl": "https://avatars.githubusercontent.com/u/58668583?v=4",
"name": "Adam Wójcik"
}
],
"references": [
Expand Down