Sitemap
Prabhu Ranganathan

Prabhu Ranganathan

14+ years of experience | Sitecore MVP | Sitecore Architect | 2 x Sitecore Certified | Ordercloud Certified | Husband & father of two

Sitecore : Helpful PowerShell Scripts

8 min readNov 8, 2023

--

Explore valuable scripts in Sitecore using SPE

Press enter or click to view image in full size

As we all know Sitecore PowerShell Extensions (SPE) module serves as a powerful Sitecore development tool, greatly enhancing your productivity and reducing the time required to deliver a Sitecore solution created by Adam Najmanowicz and Michael West.

It includes out-of-the-box reports that offer a wide range of auditing capabilities for your Sitecore solution. However in certain situations, we may find it necessary to develop custom scripts to meet specific client requirements.

Get Prabhu Ranganathan’s stories in your inbox

Join Medium for free to get updates from this writer.

Here are some custom scripts I’ve developed to meet specific client requirements. They might prove useful to others facing similar needs.

Pages Last Updated

SPE script for generating a report of Sitecore items that were last updated on or after a specified date,

  • The script finds child templates that inherit from a specified standard template.
  • It collects items that are descendants of the chosen root and meet the specified criteria.
  • Depending on the selected period (before or after), it displays items last updated before or after the specified date.
# Set the default database
$database = "master"

$root = Get-Item -Path (@{$true = "$($database):\content\home"; $false = "$($database):\content" }[(Test-Path -Path "$($database):\content\home")])

# Get the base template for filtering items
$baseTemplate = Get-Item master:\"sitecore\templates\User Defined\Web2014\Container\Pages"

$periodOptions = [ordered]@{Before = 1; After = 2; }
$maxDaysOptions = [ordered]@{"-- Skip --" = [int]::MaxValue; 30 = 30; 90 = 90; 120 = 120; 365 = 365; }

# Define settings for the user prompt
$settings = @{
Title = "Report Filter"
OkButtonName = "Proceed"
CancelButtonName = "Abort"
Description = "Filter the results for Components updated on or after the specified date"
Parameters = @(
@{
Name = "root";
Title = "Choose the report root";
Tooltip = "Only items from this root will be returned.";
},
@{
Name = "selectedDate"
Value = [System.DateTime]::Now
Title = "Date"
Tooltip = "Filter the results for items updated on or before/after the specified date"
Editor = "date time"
},
@{
Name = "selectedPeriod"
Title = "Period"
Value = 1
Options = $periodOptions
Tooltip = "Pick whether the items should have been last updated before or after the specified date"
Editor = "radio"
},
@{
Name = "selectedMaxDays"
Title = "Max Days"
Value = [int]::MaxValue
Options = $maxDaysOptions
Tooltip = "Pick the maximum number of days to include starting with the specified date"
Editor = "combo"
}
)
Icon = [regex]::Replace($PSScript.Appearance.Icon, "Office", "OfficeWhite", [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
ShowHint = $true
}

$result = Read-Variable @settings
if ($result -ne "ok") {
Exit
}

# Function to get the last updated item
function Get-LastUpdated($item) {

Write-Host $item.Paths.FullPath
Write-Log $item.Paths.FullPath

$allitems = Get-ItemReference -Path "$($database):$($item.Paths.FullPath)" | Where-Object { $_.Name -ne 'Header' -and $_.Name -ne 'HeaderView' -and $_.Name -ne 'Top-Component' -and $_.Name -ne 'Footer' -and $_.Name -ne 'FooterView' -and $_.Name -ne 'Search Box' -and $_.Name -ne 'Global Navigation' -and $_.Name -ne 'Global-Nav' -and $_.Name -ne 'Folder' -and $_.TemplateName -ne 'Device' -and $_.TemplateName -ne 'Workflow' -and $_.TemplateName -ne 'Layout' -and $_.TemplateName -ne 'Branch' -and $_.TemplateName -ne 'Meta Tag' -and $_.TemplateName -ne 'List Configuration' -and $_.TemplateName -ne 'State' } | Sort-Object __Updated -descending

Write-Log "allitems count=$($allitems.Count)"
if ($allitems.Count -ne 0) {
foreach ($associateditem in $allitems) {
if ($associateditem.HasChildren) {
$descendants = Get-ChildItem -Path "$($database):$($associateditem.Paths.FullPath)" -Recurse -WithParent | Where-Object { $_.Paths.FullPath.StartsWith('/sitecore/content/') } | Sort-Object __Updated -descending | Select -First 1
}
if ($descendants -ne $null) {
if ($lastUpdated -ne $null -and ($lastUpdated -lt $descendants.Fields[[Sitecore.FieldIDs]::Updated].Value) -or ($lastUpdated -lt $associateditem.Fields[[Sitecore.FieldIDs]::Updated].Value)) {
Write-Host $descendants.Paths.FullPath +":"+ $descendants.Fields[[Sitecore.FieldIDs]::Updated].Value
Write-Host $associateditem.Paths.FullPath +":"+ $associateditem.Fields[[Sitecore.FieldIDs]::Updated].Value

Write-Log $descendants.Paths.FullPath +"1:"+ $descendants.Fields[[Sitecore.FieldIDs]::Updated].Value
Write-Log $associateditem.Paths.FullPath +":"+ $associateditem.Fields[[Sitecore.FieldIDs]::Updated].Value

if ($descendants.Fields[[Sitecore.FieldIDs]::Updated].Value -gt $associateditem.Fields[[Sitecore.FieldIDs]::Updated].Value) {
$lastUpdated = $descendants.Fields[[Sitecore.FieldIDs]::Updated].Value
}
else {
$lastUpdated = $associateditem.Fields[[Sitecore.FieldIDs]::Updated].Value
}
}
}
else {
if ($lastUpdated -ne $null -and ($lastUpdated -lt $item.Fields[[Sitecore.FieldIDs]::Updated].Value) -or ($lastUpdated -lt $associateditem.Fields[[Sitecore.FieldIDs]::Updated].Value)) {
Write-Host $item.Paths.FullPath +":"+ $item.Fields[[Sitecore.FieldIDs]::Updated].Value
Write-Host $associateditem.Paths.FullPath +":"+ $associateditem.Fields[[Sitecore.FieldIDs]::Updated].Value

Write-Log $item.Paths.FullPath +"2:"+ $item.Fields[[Sitecore.FieldIDs]::Updated].Value
Write-Log $associateditem.Paths.FullPath +":"+ $associateditem.Fields[[Sitecore.FieldIDs]::Updated].Value

if ($item.Fields[[Sitecore.FieldIDs]::Updated].Value -gt $associateditem.Fields[[Sitecore.FieldIDs]::Updated].Value) {
$lastUpdated = $item.Fields[[Sitecore.FieldIDs]::Updated].Value
}
else {
$lastUpdated = $associateditem.Fields[[Sitecore.FieldIDs]::Updated].Value
}
}
}
}
return [Sitecore.DateUtil]::IsoDateToDateTime($lastUpdated)
}
else {
return [Sitecore.DateUtil]::IsoDateToDateTime($item.Fields[[Sitecore.FieldIDs]::Updated].Value)
}
}
filter Where-LastUpdated {
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[Sitecore.Data.Items.Item]$Item,

[datetime]$Date = ([datetime]::Today),
[switch]$IsBefore,

[int]$MaxDays
)

$convertedDate = [Sitecore.DateUtil]::IsoDateToDateTime($item.Fields[[Sitecore.FieldIDs]::Updated].Value)
$isWithinDate = $false
if ($IsBefore.IsPresent) {
if ($convertedDate -le $Date) {
$isWithinDate = $true
}
}
else {
if ($convertedDate -ge $Date) {
$isWithinDate = $true
}
}

if ($isWithinDate) {
if ($MaxDays -lt [int]::MaxValue) {
if ([math]::Abs(($convertedDate - $Date).Days) -le $MaxDays) {
$item
}
}
else {
$item
}
}
}

#base template guid
$stdTemplate = "{AB86861A-6030-46C5-B394-E8F99E8B87DB}"

$childTemplates = $baseTemplate.Axes.GetDescendants() | Where-Object { [Sitecore.Data.Managers.TemplateManager]::GetTemplate($_).InheritsFrom($stdTemplate) }
Write-Host $root.Paths.FullPath

# Iterate through child templates and filter items
foreach ($childTemplate in $childTemplates) {
$items += @($childTemplate | Get-ItemReferrer | Where-Object { $_.ItemPath.StartsWith($root.Paths.FullPath) } | Where-LastUpdated -Date $selectedDate -IsBefore:($selectedPeriod -eq 1) -MaxDays $selectedMaxDays | Initialize-Item)
}
$message = "before"
if ($selectedPeriod -ne 1) {
$message = "after"
}

if ($items.Count -eq 0) {
Show-Alert "There are no items updated on or after the specified date"
}
else {
$props = @{
Title = "Items Last Updated Report"
InfoTitle = "Items last updated $($message) date $($selectedDate)"
InfoDescription = "Lists all items last updated $($message) the date selected."
PageSize = 25
}

$items |
Show-ListView @props -Property @{Label = "Name"; Expression = { $_.DisplayName } },
@{Label = "Actual Last Updated"; Expression = { $_.__Updated } },
@{Label = "Last Published"; Expression = { $_."__Last Published" } },
@{Label = "Related Item Last Updated"; Expression = { Get-LastUpdated($_) } },
@{Label = "Updated by"; Expression = { $_."__Updated by" } },
@{Label = "Created"; Expression = { $_.__Created } },
@{Label = "Created by"; Expression = { $_."__Created by" } },
@{Label = "Template Name"; Expression = { $_.TemplateName } },
@{Label = "Path"; Expression = { $_.ItemPath } }
}
Close-Window

Get All Users Activity

SPE script to retrieve user information from the ASP.NET Membership database and display it in a tabular format with the below properties,

  • User: User’s username.
  • Is Online: Indicates whether the user is currently online.
  • Creation Date: The date when the user account was created.
  • Last Login Date: The date of the user’s last login.
  • Last Activity Date: The date of the user’s last activity
# Retrieve user information from the ASP.NET Membership database
$users = [System.Web.Security.Membership]::GetAllUsers()

# Define a list of properties to display
$properties = @(
@{ Label = "User"; Expression = { $_.UserName } },
@{ Label = "Is Online"; Expression = { $_.IsOnline } },
@{ Label = "Creation Date"; Expression = { $_.CreationDate } },
@{ Label = "Last Login Date"; Expression = { $_.LastLoginDate } },
@{ Label = "Last Activity Date"; Expression = { $_.LastActivityDate } }
)

# Display user information in a tabular format
$users | Show-ListView -Property $properties -Title "User Information" -PageSize 25

Find and Delete Unused MediaItems

SPE script to to identify and delete unused media items within a Sitecore database. This script can be useful for cleaning up a Sitecore media library by identifying and removing media items that are not used within the content of your Sitecore instance.

# Specify the database 
$databaseName = "master"

# Get the Sitecore database
$database = Get-Database -Name $databaseName

# Define a list to store unused media items
$unusedMediaItems = @()

# Get all media library items
$mediaLibraryPath = "/sitecore/media library"
$mediaLibrary = Get-Item -Path "$($database.Name):$mediaLibraryPath"

# Iterate through media library items
$mediaLibrary.Axes.GetDescendants() | ForEach-Object {
$mediaItem = $_

# Check if the media item is referenced by any content item
$referenced = Get-ItemReferrer -Item $mediaItem | Where-Object { $_.Paths.FullPath -notlike "$($mediaLibrary.Paths.FullPath)/*" }

if ($referenced -eq $null) {
$unusedMediaItems += $mediaItem
}
}

# Display the list of unused media items
$unusedMediaItems | Format-Table -Property @{Label = "Media Item"; Expression = { $_.Paths.FullPath } }

Write-Host "List of unused media items:"
$unusedMediaItems.Count

# Delete unused media items (uncomment the following code to delete them)
# foreach ($unusedItem in $unusedMediaItems) {
# Remove-Item -Path $unusedItem.Paths.FullPath
# }
# Write-Host "Deleted $unusedMediaItems.Count unused media items."

Note: Be cautious when enabling the deletion code, as it will permanently remove the identified unused media items. It is advisable to thoroughly test the script.

Lock a User

This SPE script helps to temporarily or permanently disable a user’s access to the Sitecore CMS. If the specified user doesn’t exist in the Membership database, the script displays a message indicating that the user was not found.

# Specify the username of the user you want to lock
$usernameToLock = "UsernameToLock"

# Lock the user
$user = [System.Web.Security.Membership]::GetUser($usernameToLock)
if ($user -ne $null) {
$user.UnlockUser()
$user.IsApproved = $false
[System.Web.Security.Membership]::UpdateUser($user)
Write-Host "User '$usernameToLock' has been locked."
}
else {
Write-Host "User '$usernameToLock' not found."
}

Unlock a User

This SPE script helps to enable a user’s access to the Sitecore CMS.

# Specify the username of the user you want to unlock
$usernameToUnlock = "UsernameToUnlock"

# Unlock the user
$user = [System.Web.Security.Membership]::GetUser($usernameToUnlock)
if ($user -ne $null) {
$user.UnlockUser()
$user.IsApproved = $true
[System.Web.Security.Membership]::UpdateUser($user)
Write-Host "User '$usernameToUnlock' has been unlocked."
}
else {
Write-Host "User '$usernameToUnlock' not found."
}

Lock and Unlock specific Domain users

This SPE script makes life easier by taking care of user account management during a content freeze on deployments or some other situation. It ensures that non-admin users can’t make changes during a content freeze and re-enables them afterward.

# Specify the Sitecore domain
$domainName = "extranet"

# Get all users in the specified domain
$users = Get-User -Domain $domainName

# Specify the username of the admin user
$adminUsername = "admin"

# Check if a content freeze is active
$contentFreezeActive = $false

if ($contentFreezeActive) {
# Content freeze is active, lock non-admin users
foreach ($user in $users) {
if ($user.UserName -ne $adminUsername) {
$user | Lock-UserAccount
Write-Host "User locked: $($user.UserName)"
}
}

Write-Host "Non-admin users have been locked during the content freeze."
}
else {
# Content freeze is over, unlock previously locked non-admin users
foreach ($user in $users) {
if ($user.UserName -ne $adminUsername) {
$user | Unlock-UserAccount
Write-Host "User unlocked: $($user.UserName)"
}
}

Write-Host "Previously locked non-admin users have been unlocked after the content freeze."
}

Unlock the Locked Items

This SPE script written to help when you are dealing with a situation where multiple items in the Sitecore database are locked, and you need to unlock them in a batch.

# Specify the database
$databaseName = "master"

# Get the Sitecore database
$database = Get-Database -Name $databaseName

# Get all locked items in the database
$lockedItems = Get-Item -Path "$($database.Name):/sitecore" -Language * -Version * | Where-Object { $_.Locking.IsLocked() }

# Unlock each locked item
foreach ($item in $lockedItems) {
if ($item.Locking.HasLock()) {
$item.Editing.EndEdit()
Write-Host "Unlocked: $($item.Paths.FullPath)"
}
}

Write-Host "All locked items have been unlocked."

Unpublished Items

This SPE script helps to provide the list of items that have never been published or have been unpublished in the web database.

# Specify the database 
$databaseName = "master"

# Get the Sitecore database
$database = Get-Database -Name $databaseName

# Get all items that are not published
$unpublishedItems = Get-Item -Path "$($database.Name):/sitecore/content/Home" -Language * -Version * | Where-Object { $_.Publishing.NeverPublish }

# Display the list of unpublished items
$unpublishedItems | Format-Table -Property @{Label = "Item Path"; Expression = { $_.Paths.FullPath } }

Write-Host "List of unpublished items:"
$unpublishedItems.Count

You can find all my scripts in my GitHub repository,

I believe these scripts can be valuable for someone facing similar needs. However, before applying these scripts in any environment, it’s crucial to keep in mind that a thorough review is essential.

Happy learning!! Happy Sharing!!

--

--

Prabhu Ranganathan
Prabhu Ranganathan