Power Of Sitecore Module In XM Cloud

In this blog, we will explore how to automate the process of creating roles with appropriate permissions when working in a multilingual and multisite environment. As soon as we think about automation, PowerShell scripts are often the first tool that comes to mind.

And you’re absolutely right—we will be using PowerShell scripts, but with a twist. We'll leverage Sitecore Modules to approach this differently, integrating the module into the scaffolding definition. This way, whenever a new site is created in XM Cloud, roles and permissions will be automatically set up. So let's quickly start.

Before we move forward, I recommend reading my previous two blogs on role creation and assigning proper permissions, which are explained through a specific use case. We'll be using the same use case for our purposes here.

We will divide the blog into 2 section.

Creating the Scripts in Script Library

We will create required scripts under script library as shown in below screenshot. Navigate to /sitecore/system/Modules/PowerShell/Script Library and add a new PowerShell Script Module Folder. In our case we have given the name as Test Site Setup Module.

Now add a PowerShell Script Module under above created item. In our case we have given the name as TestHeadless Setup.

Now add a PowerShell Library under above created item. In our case we have given the name as Functions. This will hold our scripts for creating roles and permissions.

Now add two PowerShell Scripts names as Configure Roles and Update Permissions

Roles Scripts

       
Import-Function -Name Invoke-ConfigureRole

function Invoke-Step {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 0 )]
        [Sitecore.XA.Foundation.Scaffolding.Models.CreateNewSiteModel]$Model
    )
    
    begin {
        Write-Verbose "Cmdlet Invoke-Validation - Begin"
    }

    process {
        Write-Verbose "Cmdlet Invoke-Validation - Process"
              
		$siteName =$Model.SiteName
		$siteType = "External"
        $Organization = "ABC"
		 write-host "Sitename: " $siteName
		Invoke-ConfigureRole $siteName $siteType
	}

    end {
        Write-Verbose "Cmdlet Invoke-Validation - End"
    }
}
	   

Assigning Permissions Scripts

       
Import-Function -Name Invoke-SetRolePermissions

function Invoke-Step {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 0 )]
        [Sitecore.XA.Foundation.Scaffolding.Models.CreateNewSiteModel]$Model
    )
    
    begin {
        Write-Verbose "Cmdlet Invoke-Validation - Begin"
    }

    process {
        Write-Verbose "Cmdlet Invoke-Validation - Process"
		$siteName = $Model.SiteName
		$site = $Model.SiteLocation.Children | ? { $_.Name -eq $siteName } | Select-Object -First 1 | % { Get-Item . -ID $_.ID -Language $Model.Language }
		write-host "Item Start Path: " $site.Paths.Path
		$sitestartPath = $site.Paths.Path
		$sitestart = Get-Item -Path "$sitestartPath" 
		$siteregionItem = $sitestart.Parent
		write-host "Parent Item Start Path: " $siteregionItem.Name

                $siteLanguages = $Model.Language
		write-host "Language: " $siteLanguages

		$siteType = "External"
       $Organization = "ABC"
		$region = $siteregionItem.Name
		$rolePrefix = "$Organization $siteType $siteName"

                Invoke-SetRolePermissions $siteLanguages $rolePrefix $region $siteType $siteName
	}

    end {
        Write-Verbose "Cmdlet Invoke-Validation - End"
    }
}
	   

Now let's create the Invoke-ConfigureRole and Invoke-SetRolePermissions. Navigate to /sitecore/system/Modules/PowerShell/Script Library/Test Site Setup Module and PowerShell Script Module. In our case we named as Security And Permissions.Under Security and Permissions, create PowerShell Script Library named Functions and then again create a PowerShell Script Library called Cmdlets.under that create the required scripts Invoke-ConfigureRole and Invoke-SetRolePermissions

Invoke-ConfigureRole function

       
function Invoke-ConfigureRole {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 0 )]
        [String]$siteName,
        [Parameter(Mandatory = $true, Position = 1 )]
        [String]$siteType
    )

    begin {
        Write-Verbose "Cmdlet ConfigureRole- Begin"
    }

    process {
        Write-Verbose "Cmdlet ConfigureRole- Process"
                       write-host "Sitename Inside Configure role main script: " $siteName
                       write-host "SiteType Inside Configure role main script: " $siteType

		    $rolePrefix = "ABC $siteType $siteName"
            $siteAccessRole = "$rolePrefix Access"
			$siteAdminRole = "$rolePrefix Site Admin"
			$contentAdminRole = "$rolePrefix Content Admin"
			$contentContributorRole = "$rolePrefix Content Contributor"
			$contentPublisherRole = "$rolePrefix Content Publisher"
			$contentPreviwerRole = "$rolePrefix Content Previewer"

			New-Role -Identity $siteAccessRole
			Add-RoleMember -Identity "ABC Limiter" -Members $siteAccessRole

			New-Role -Identity $contentPreviwerRole
			Add-RoleMember -Identity $siteAccessRole -Members $contentPreviwerRole
			Add-RoleMember -Identity "Designer" -Members $contentPreviwerRole

			New-Role -Identity $contentContributorRole
			Add-RoleMember -Identity $contentPreviwerRole -Members $contentContributorRole
			Add-RoleMember -Identity "ABC Author" -Members $contentContributorRole

			New-Role -Identity $contentPublisherRole
			Add-RoleMember -Identity $contentContributorRole -Members $contentPublisherRole
			Add-RoleMember -Identity "Sitecore Client Publishing" -Members $contentPublisherRole

			New-Role -Identity $contentAdminRole
			Add-RoleMember -Identity $contentContributorRole -Members $contentAdminRole
			Add-RoleMember -Identity "ABC Approver" -Members $contentAdminRole
			Add-RoleMember -Identity "Sitecore Client Publishing" -Members $contentAdminRole

			New-Role -Identity $siteAdminRole
			Add-RoleMember -Identity $contentAdminRole -Members $siteAdminRole

    }

    end {
        Write-Verbose "Cmdlet ConfigureRole- End"
    }
}
	   

Invoke-SetRolePermissions

       
       function Invoke-SetRolePermissions {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 0 )]
        [String]$siteLanguages,
        [Parameter(Mandatory = $true, Position = 1 )]
        [String]$rolePrefix,
        [Parameter(Mandatory = $true, Position = 2 )]
        [String]$region,
        [Parameter(Mandatory = $true, Position = 3 )]
        [String]$siteType,
	    [Parameter(Mandatory = $true, Position = 4 )]
        [String]$siteName
    )

    begin {
        Write-Verbose "Cmdlet SetRolePermissions - Begin"
    }

    process {
        Write-Verbose "Cmdlet SetRolePermissions - Process"
		write-host "the input langauge is :" $siteLanguages
		write-host "The rolePrefix: " $rolePrefix 
		write-host "The region: "  $region 
		write-host "the siteType: "  $siteType 
		write-host "The sitename: " $siteName 
		$Organization = "ABC"
        $siteAccessRole = "$rolePrefix Access"
		$siteAdminRole = "$rolePrefix Site Admin"
		$contentAdminRole = "$rolePrefix Content Admin"
		$contentContributorRole = "$rolePrefix Content Contributor"
		$contentPublisherRole = "$rolePrefix Content Publisher"
		$contentPreviewerRole = "$rolePrefix Content Previewer"

		#Content
		$regionPath = "/sitecore/content/$Organization $siteType/$region"
		write-host "Region Item: " $regionPath
		$sitePath = "$regionPath/$siteName"
		write-host "Home Item: " $sitePath
		$homePath = "$sitePath/Home"
		$mediaPath = "$sitePath/Media"
		$dataPath = "$sitePath/Data"
		$presentationPath = "$sitePath/Presentation"
		$dictionaryPath = "$sitePath/Dictionary"
		$SettingsPath = "$sitePath/Settings"

		#Media
		$mediaTenantPath = "/sitecore/media library/Project/$Organization $siteType"
		$mediaRegionPath = "$mediaTenantPath/$region"
		$mediaSitePath = "$mediaRegionPath/$siteName"
		$mediaSharedPath = "$mediaTenantPath/shared"
		$mediaSystemPath = "/sitecore/media library/System"

		#System
		$publishingTarget = "/sitecore/system/Publishing targets/Edge"
		$languages = "/sitecore/system/Languages"

		#Access
		$allowItemReadSiteAccess = New-ItemAcl -AccessRight item:read -PropagationType Entity -SecurityPermission AllowAccess -Identity $siteAccessRole
		$denyInheritanceReadSiteAccess = New-ItemAcl -AccessRight item:read -PropagationType Descendants -SecurityPermission DenyAccess -Identity $siteAccessRole
		$allowAnyReadSiteAccess = New-ItemAcl -AccessRight item:read -PropagationType Any -SecurityPermission AllowAccess -Identity $siteAccessRole
		$allowLanguageReadSiteAccess = New-ItemAcl -AccessRight language:read -PropagationType Any -SecurityPermission AllowAccess -Identity $siteAccessRole

		Get-Item -Path $regionPath | Add-ItemAcl -AccessRules $allowItemReadSiteAccess, $denyInheritanceReadSiteAccess -PassThru
		Get-Item -Path $sitePath | Add-ItemAcl -AccessRules $allowAnyReadSiteAccess -PassThru
		Get-Item -Path $mediaTenantPath | Add-ItemAcl -AccessRules $allowItemReadSiteAccess, $denyInheritanceReadSiteAccess -PassThru
		Get-Item -Path $mediaRegionPath | Add-ItemAcl -AccessRules $allowItemReadSiteAccess, $denyInheritanceReadSiteAccess -PassThru
		Get-Item -Path $mediaSitePath | Add-ItemAcl -AccessRules $allowAnyReadSiteAccess -PassThru
		Get-Item -Path $mediaSharedPath | Add-ItemAcl -AccessRules $allowAnyReadSiteAccess -PassThru
		Get-Item -Path $mediaSystemPath | Add-ItemAcl -AccessRules $allowAnyReadSiteAccess -PassThru

		foreach ($language in $siteLanguages) {
			Get-Item -Path "$languages/$language" | Add-ItemAcl -AccessRules $allowItemReadSiteAccess, $allowLanguageReadSiteAccess -PassThru
		}

		#Site Admin
		$allowAnyReadSiteAdmin = New-ItemAcl -AccessRight item:read -PropagationType Any -SecurityPermission AllowAccess -Identity $siteAdminRole
		$allowAnyWriteSiteAdmin = New-ItemAcl -AccessRight item:write -PropagationType Any -SecurityPermission AllowAccess -Identity $siteAdminRole
		$allowAnyRenameSiteAdmin = New-ItemAcl -AccessRight item:rename -PropagationType Any -SecurityPermission AllowAccess -Identity $siteAdminRole
		$allowAnyCreateSiteAdmin = New-ItemAcl -AccessRight item:create -PropagationType Any -SecurityPermission AllowAccess -Identity $siteAdminRole
		$allowAnyDeleteSiteAdmin = New-ItemAcl -AccessRight item:delete -PropagationType Any -SecurityPermission AllowAccess -Identity $siteAdminRole

		Get-Item -Path $sitePath | Add-ItemAcl -AccessRules $allowAnyReadSiteAdmin, $allowAnyWriteSiteAdmin, $allowAnyRenameSiteAdmin, $allowAnyCreateSiteAdmin, $allowAnyDeleteSiteAdmin -PassThru
		Get-Item -Path $presentationPath | Add-ItemAcl -AccessRules $allowAnyReadSiteAdmin, $allowAnyWriteSiteAdmin, $allowAnyRenameSiteAdmin, $allowAnyCreateSiteAdmin, $allowAnyDeleteSiteAdmin -PassThru
		Get-Item -Path $dictionaryPath | Add-ItemAcl -AccessRules $allowAnyReadSiteAdmin, $allowAnyWriteSiteAdmin, $allowAnyRenameSiteAdmin, $allowAnyCreateSiteAdmin, $allowAnyDeleteSiteAdmin -PassThru
		Get-Item -Path $settingsPath | Add-ItemAcl -AccessRules $allowAnyReadSiteAdmin, $allowAnyWriteSiteAdmin, $allowAnyRenameSiteAdmin, $allowAnyCreateSiteAdmin, $allowAnyDeleteSiteAdmin -PassThru
		Get-Item -Path $mediaSitePath | Add-ItemAcl -AccessRules $allowAnyReadSiteAdmin, $allowAnyWriteSiteAdmin, $allowAnyRenameSiteAdmin, $allowAnyCreateSiteAdmin, $allowAnyDeleteSiteAdmin -PassThru
		Get-Item -Path $mediaSharedPath | Add-ItemAcl -AccessRules $allowAnyReadSiteAdmin, $allowAnyWriteSiteAdmin, $allowAnyRenameSiteAdmin, $allowAnyCreateSiteAdmin, $allowAnyDeleteSiteAdmin -PassThru

		#Content Admin
		$allowAnyReadSiteAdmin = New-ItemAcl -AccessRight item:read -PropagationType Any -SecurityPermission AllowAccess -Identity $contentAdminRole
		$allowDescendantsRenameContentAdmin = New-ItemAcl -AccessRight item:rename -PropagationType Descendants -SecurityPermission AllowAccess -Identity $contentAdminRole
		$allowDescendantsDeleteContentAdmin = New-ItemAcl -AccessRight item:delete -PropagationType Descendants -SecurityPermission AllowAccess -Identity $contentAdminRole

		Get-Item -Path $homePath | Add-ItemAcl -AccessRules $allowDescendantsRenameContentAdmin, $allowDescendantsDeleteContentAdmin -PassThru
		Get-Item -Path $mediaPath | Add-ItemAcl -AccessRules $allowDescendantsRenameContentAdmin, $allowDescendantsDeleteContentAdmin -PassThru
		Get-Item -Path $dataPath | Add-ItemAcl -AccessRules $allowDescendantsRenameContentAdmin, $allowDescendantsDeleteContentAdmin -PassThru
		Get-Item -Path $mediaSitePath | Add-ItemAcl -AccessRules $allowDescendantsRenameContentAdmin, $allowDescendantsDeleteContentAdmin -PassThru
		Get-Item -Path $mediaSharedPath | Add-ItemAcl -AccessRules $allowDescendantsRenameContentAdmin, $allowDescendantsDeleteContentAdmin -PassThru
		Get-Item -Path $publishingTarget | Add-ItemAcl -AccessRules $allowAnyReadSiteAdmin -PassThru

		#Content Contributor
		$allowAnyReadContentContributor = New-ItemAcl -AccessRight item:read -PropagationType Any -SecurityPermission AllowAccess -Identity $contentContributorRole
		$allowAnyWriteContentContributor = New-ItemAcl -AccessRight item:write -PropagationType Any -SecurityPermission AllowAccess -Identity $contentContributorRole
		$allowAnyCreateContentContributor = New-ItemAcl -AccessRight item:create -PropagationType Any -SecurityPermission AllowAccess -Identity $contentContributorRole
		$allowDescendantsCreateContentContributor = New-ItemAcl -AccessRight item:create -PropagationType Descendants -SecurityPermission AllowAccess -Identity $contentContributorRole
		$allowLanguageWriteContentContributor = New-ItemAcl -AccessRight language:write -PropagationType Any -SecurityPermission AllowAccess -Identity $contentContributorRole

		Get-Item -Path $homePath | Add-ItemAcl -AccessRules $allowAnyWriteContentContributor, $allowAnyCreateContentContributor -PassThru
		Get-Item -Path $mediaPath | Add-ItemAcl -AccessRules $allowAnyWriteContentContributor, $allowAnyCreateContentContributor -PassThru
		Get-Item -Path $dataPath | Add-ItemAcl -AccessRules $allowAnyWriteContentContributor, $allowDescendantsCreateContentContributor -PassThru
		Get-Item -Path "$presentationPath/Partial Designs" | Add-ItemAcl -AccessRules $allowAnyWriteContentContributor, $allowDescendantsCreateContentContributor -PassThru
		Get-Item -Path "$settingsPath/Redirects" | Add-ItemAcl -AccessRules $allowAnyReadContentContributor, $allowAnyWriteContentContributor, $allowDescendantsCreateContentContributor -PassThru
		Get-Item -Path $mediaSitePath | Add-ItemAcl -AccessRules $allowAnyWriteContentContributor, $allowAnyCreateContentContributor -PassThru
		Get-Item -Path $mediaSharedPath | Add-ItemAcl -AccessRules $allowAnyWriteContentContributor, $allowAnyCreateContentContributor -PassThru

		foreach ($language in $siteLanguages) {
			Get-Item -Path "$languages/$language" | Add-ItemAcl -AccessRules $allowLanguageWriteContentContributor -PassThru
		}

		#Content Publisher
		$allowAnyReadContentPublisher = New-ItemAcl -AccessRight item:read -PropagationType Any -SecurityPermission AllowAccess -Identity $contentPublisherRole

		Get-Item -Path $publishingTarget | Add-ItemAcl -AccessRules $allowAnyReadContentPublisher -PassThru

		#Content Previewer
		$allowAnyReadContentContributor = New-ItemAcl -AccessRight item:read -PropagationType Any -SecurityPermission AllowAccess -Identity $contentPreviewerRole
		$denyAnyWriteContentContributor = New-ItemAcl -AccessRight item:write -PropagationType Any -SecurityPermission DenyAccess -Identity $contentPreviewerRole
		$denyAnyRenameContentContributor = New-ItemAcl -AccessRight item:rename -PropagationType Any -SecurityPermission DenyAccess -Identity $contentPreviewerRole
		$denyAnyCreateContentContributor = New-ItemAcl -AccessRight item:create -PropagationType Any -SecurityPermission DenyAccess -Identity $contentPreviewerRole
		$denyAnyDeleteContentContributor = New-ItemAcl -AccessRight item:delete -PropagationType Any -SecurityPermission DenyAccess -Identity $contentPreviewerRole
		$denyAnyAdministerContentContributor = New-ItemAcl -AccessRight item:admin -PropagationType Any -SecurityPermission DenyAccess -Identity $contentPreviewerRole

		Get-Item -Path $sitePath | Add-ItemAcl -AccessRules $denyAnyWriteContentContributor, $denyAnyRenameContentContributor, $denyAnyCreateContentContributor, $denyAnyDeleteContentContributor, $denyAnyAdministerContentContributor -PassThru
		Get-Item -Path $homePath | Add-ItemAcl -AccessRules $allowAnyReadContentContributor -PassThru
    }

    end {
        Write-Verbose "Cmdlet SetRolePermissions - End"
    }
}
	   

Creating the functions separately gives use the benefits if reusablity.The scripts are self explanatory. If still you have doubts, then read the above blogs which have detailed explanations. Now our Script part is completed. We will focus on Module creation.

Creating the Module

Navigate to below path /sitecore/system/Settings/Project and a folder called Testing Module. Right Click the folder and add item created from template /sitecore/system/Settings/Project/Testing Module/Test Headless Site Setup. We have named it Test Headless Site Setup.

The item will have field called name Title, give it a suitable name which will be shown during the site creation process under modules tab. There are other fields and what are they used for in reference section link.

Now right click the Test Headless Site Setup and add PostSetupSetp option as shown in below screenshot.

Now add two PostSetup Step ConfigureRole and ConfigurePermission

ConfigureRole Step --> Just Select the configure roles script created under Functions as shown below.

ConfigurePermission Step --> Just Select the Update Permissions script created under Functions as shown below.

Now go to your Site Collection and add a headless site. In module tab you will see our newly created Test Headless Site Setup skeleton. Just check the checkbox and as soon as your new website is created, you will see the newly created roles and permissions in Role Manager.

Thanks for reading and let the learning continue. I have attached an example above like what all can be included in dependencies when you create Test Headless Site Setup. We can add the default module dependencies that will execute before our custom modules.

You can check my other blogs too if interested. Blog Website

References:

Comments

Popular posts from this blog

Sitecore XM Cloud Form Integration with Azure Function as Webhook

Automate RSS Feed to Sitecore XM Cloud: Logic App, Next.js API & Authoring API Integration

Create and Fetch Content From Sitecore Content Hub One using GraphQL and React