GitHub Icon Image
GitHub

Create Site Columns and add to Content Types

Summary

The script will create a Site Columns by checking whether it exists or not. Adds the site columns to the existing Content Types.

  • PnP PowerShell
  • CLI for Microsoft 365

function CreateField {
    param ([string] $FieldName, [string] $FieldXML)

    try {
        #Check the field and create
        $field = Get-PnPField -Identity $FieldName -ErrorAction SilentlyContinue

        If ($Null -eq $field) {
            Add-PnPFieldFromXml -FieldXml $FieldXML
            Write-host "Successfully created field '$FieldName'" -ForegroundColor Green
        }
        Else {
            Write-host "Field '$FieldName' already exists. Skipping creation" -f Green
        }
    }
    catch {
        Write-Error "Error in CreateField - $_"
        throw
    }
}

function CreateAndAddFieldsToCTs {
    param ([string[]] $ContentTypes)

    try {

        #region Create Site Columns
        $fieldsToCreate = @(
            @{
                FieldName = "admin_review"
                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"
                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' />"
            }
        )

        foreach ($fieldToCreate in $fieldsToCreate) {
            Write-Host "Creating the field - $($fieldToCreate.FieldName)" -ForegroundColor Yellow
            CreateField -FieldName $($fieldToCreate.FieldName) -FieldXML $($fieldToCreate.FieldXML)
        }
        Write-Host `n `n
        #endregion

        foreach ($contentType in $ContentTypes) {
            foreach ($fieldToCreate in $fieldsToCreate) {
                Write-Host "Adding field - $($fieldToCreate.FieldName) to CT $contentType" -ForegroundColor Yellow
                Add-PnPFieldToContentType -Field $($fieldToCreate.FieldName) -ContentType $contentType
            }
            Write-Host "Successfully added fields to CT $contentType" -ForegroundColor Green
            Write-Host `n
        }
    }
    catch {
        Write-Error "Error in CreateAndAddFieldsToCTs - $_"
    }
}

try {
    $clientId = ""
    $clientSecret = ""
    $siteUrl = ""
    Connect-PnPOnline -Url $siteUrl -ClientId $clientId -ClientSecret $clientSecret

    CreateAndAddFieldsToCTs -ContentTypes @("My Review CT", "Global User Review CT")
}
catch {
    Write-Error "Something wrong: " $_
}
finally {
    $pnpConnection = Get-PnPConnection -ErrorAction SilentlyContinue
    if ($pnpConnection) {
        Disconnect-PnPOnline
    }
}

Check out the PnP PowerShell to learn more at: https://aka.ms/pnp/powershell

The way you login into PnP PowerShell has changed please read PnP Management Shell EntraID app is deleted : what should I do ?

# .\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' />" }
        )
    }

    $Script:Results = [System.Collections.Generic.List[pscustomobject]]::new()

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

        $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
        }

        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
        }

        $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
        }

        $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
        )

        $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
        }

        $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
        }

        $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"
            })
    }
}

process {
    foreach ($definition in $FieldDefinitions) {
        Ensure-SiteField -WebUrl $SiteUrl -Definition $definition
    }

    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
}

Check out the CLI for Microsoft 365 to learn more at: https://aka.ms/cli-m365

Important changes coming to the way you login into CLI for Microsoft 365 (effective 9th September 2024) see Changes in PnP Management Shell registration in Microsoft 365

Contributors

Author(s)
Ahamed Fazil Buhari
Ganesh Sanap
Adam Wójcik

Disclaimer

THESE SAMPLES ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

Back to top Script Samples
Generated by DocFX with Material UI