Streamline Sitecore CLI with PowerShell Scripts

While working on a Sitecore XM Cloud project for a client, I ran into a common challenge: I didn’t have access to the XM Cloud Deploy app, so I had to rely entirely on the Sitecore CLI. While the CLI is powerful, I found that remembering all the necessary commands, parameters, and environment IDs quickly became tedious.

Not having access to XM Cloud Deploy App was no fun, but I had to make the best of what I had. I thought about how I could make the most out of these limitations, and decided I had might as well write some PowerShell scripts to make my life easier! Not only did these make things easier, I even gravitated to using them on other projects where I did have XM Cloud Deploy access!

In this blog post, I’ll showcase some quick and easy scripts that you can use when using the Sitecore CLI. My hope is that these will save you some time when using the CLI.

At first, I focused on simplifying the most repetitive commands. The two first scripts I created were Login and GetEnv.

Login

# Logs into Sitecore using client credentials.
Function Login {
    dotnet sitecore cloud login --client-credentials --client-id "<CLIENT_ID_HERE>" --client-secret "<CLIENT_PASSWORD_HERE>"
}

This script is as simple as it sounds. It logs you into Sitecore via the CLI. Since I didn’t have access to XM Cloud Deploy app, I needed to login using client credentials on a regular basis. I was using client credentials limited strictly for development. So, I simply hard-coded these in. I would not recommend this for production client credentials. However, you could pull these into the script in a more secure way where needed.

With Login out of the way, I wanted a way to quickly scope to specific environments without having to remember (or look for) long environment IDs.

GetEnv

# Retrieves the environment ID for a given environment.
Function GetEnv {
    param(
            [Parameter(Mandatory=$false)][string]$Env
        )
        $environmentId = "<DEV_ENV_ID>"
    if($Env -eq $null -or $Env -eq ""){
            $Env = "dev"
        }

        $envToLower = $Env.ToLower()

        if($envToLower -eq "test" -or $envToLower -eq "tst"){
            $environmentId = "<TEST_ENV_ID>"
        }
        elseif($envToLower -eq "prod" -or $envToLower -eq "prd" -or $envToLower -eq "live"){
            $environmentId = "<PROD_ENV_ID>"
    }

    return $environmentId
}

GetEnv is also very simple. This script simply uses an environment’s common name, a parameter (-Env) and passes it through to a conditional check that returns the environment ID associated with that name. This function can be called from other scripts that use Sitecore CLI commands that require an Environment ID parameter!

In my example above, I have 3 environments: Dev, Tst and Prod. If no parameter is set, the script returns the Environment id for the Dev environment. You can use whatever names you’d like by adding/removing/changing the conditional checks.

With these two functions out of the way, it was time to get into the nitty gritty and start writing the more complex scripts. Remember, my main goal was to save time and streamline the process as much as I could.

GetLatestDeployment

# Gets the latest deployment based off of the provided environment.
Function GetLatestDeployment {
    param(
         [Parameter(Mandatory=$false)][string]$Env
    )
    if($Env -eq $null -or $Env -eq ""){
        $Env = "dev"
    }

    $envToLower = $Env.ToLower()
    $environmentId = GetEnv -Env $Env

    Write-Host "Getting latest deployment from:" $Env   

    $deployments = dotnet sitecore cloud deployment list --environment-id $environmentId
        $length = $deployments | Measure-Object -Line
        $latestDeploymentStartLine = $length.Lines - 32
        $totalLines = 31
        $lineCount = 0

        $firstString = "Deployment Id                     : "
        $lastString = "LSOrgClientID"

        $pattern = "$firstString(.*?)$lastString"
        $results = [regex]::Matches($deployments, $pattern)
        $deploymentCount = $results.Count

        $stringArray = @("Started at                        :",
                         "Completed at                      :", 
                         "Provisioning status               :",
                         "Provisioning started at           :",
                         "Provisioning completed at         :",
                         "Provisioning last failure message :",
                         "Build status                      :",
                         "Building started at               :",
                         "Building completed at             :",
                         "Build last failure message        :",
                         "Deployment status                 :",
                         "Deployment started at             :",
                         "Deployment completed at           :",
                         "Deployment last failure message   :",
                         "Post Action status                :",
                         "Post Action started at            :",
                         "Post Action completed at          :",
                         "Post Action last failure message  :",
                         "Calculated status                 :",
                         "Created by                        :"

                         )

        # Get the latest deployment and it's id.
        $latestDeploymentId = $results[$deploymentCount - 1].Value

        ForEach($message in $stringArray){
            $latestDeploymentId = $latestDeploymentId.Replace($message, "`n" + $message)
        }


        Write-Output $latestDeploymentId
}

Because I did not have access to XM Cloud Deploy App, I had to completely rely on the CLI to monitor deployments. As many of you know, early on in a project’s development, it’s not uncommon to encounter build and deploy errors on a regular basis. Typically with an XM Cloud project, if a deployment failed I would just open the deploy app and take a look to see what was wrong. Without access however, is a different beast entirely.

One issue I found is that there was not a way to simply get the last-run deployment for an environment, it was either all deployment history or no deployment history. Printing out all of the data for every deployment quickly flooded my PowerShell console and became difficult to parse through. So I got to work and wrote the above script.

While it looks a little hacky, it does in fact work. The script will call the CLI deployment list command using the environment id as a parameter (remember our GetEnv command?) and then strip out everything not related to the last deployment. This leaves us with a single deployment output, making it very easy to see if it’s still running, failed or completed.

But what if it did fail? We still wouldn’t know why with the above script, so in comes GetLatestDeploymentLog

GetLatestDeploymentLog

# Gets the latest deployment from XM Cloud Deploy, and then retrieves its log file.
Function GetLatestDeploymentLog {
param(
         [Parameter(Mandatory=$false)][string]$Env
    )
    if($Env -eq $null -or $Env -eq ""){
        $Env = "dev"
    }

    $envToLower = $Env.ToLower()
    $environmentId = GetEnv -Env $Env

    Write-Host "Getting latest deployment log from:" $Env

    # Get the latest deployment.
    $deployments = dotnet sitecore cloud deployment list --environment-id $environmentId

    $length = $deployments | Measure-Object -Line
    Write-Host $length.Lines
    if($length.Lines -gt 1){
        Write-Host "Found deployment."
        $deploymentId = $length.Lines - 32
        $line = $deployments | Select-Object -Index ($deploymentId)

        $firstString = "Deployment Id                     : "
        $lastString = " Started"

        $pattern = "$firstString(.*?)$lastString"
        $results = [regex]::Matches($deployments, $pattern)
        $deploymentCount = $results.Count

        # Get the latest deployment and it's id.
        $latestDeploymentId = $results[$deploymentCount - 1].Value.Replace($firstString, "").Replace($lastString, "")

        # Retrieve the logs.
        dotnet sitecore cloud deployment log --deployment-id $latestDeploymentId --outputpath "C:\<PATH_TO_STORE_LOG_FILES>"
        
    }
    else{
        Write-Host -ForegroundColor Red "An error has occured. Please try running 'Login' first and then try again."
    }
}

This script is my favorite of the bunch, as it does a ton of heavy lifting. Where you would need to call several individual CLI commands, this script does it all in one. Normally, you would need to do the following in order to download a deployment log:

  1. Get a list of deployments (dotnet sitecore cloud deployment list)
  2. Extract the latest deployment id.
  3. Download the log (dotnet sitecore cloud deployment log).

The above script does all that for you. I found this incredibly helpful when debugging build and deploy issues. I did not have to manually search for the latest deployment id. I also avoided setting output paths, etc. This ended up saving me an unbelievable amount of time over the course of this project!

Some other honorable mentions are scripts for creating/updating or deleting environment variables, serializing content and downloading general log files. I’ve pasted the full PowerShell module below, which includes all of the above and few more!

If you want to use this PowerShell module, you can set it up as follows:

  1. Download the script below and save it as Commands.psm1 in the root of your project’s repository.
  2. Replace any variables in the script. These are noted with angle brackets (i.e: <DEV_ENV_ID>.
    • You may also wish to change the names or number of environments themselves, see the conditional checks within GetEnv.
  3. Open a new PowerShell console window and navigate to your repository root.
  4. Run Import-Module .\Commands.psm1 in the PowerShell console.
    • You may need to enable permissions to use Import-Module.
  5. Optional: Run InstallDotNet (a function within Commands.psm1) to install the dotnet tool for Sitecore.CLI.
    • You should only need to do this one, and only if you haven’t already installed Sitecore CLI and its prerequisites.
  6. Run any command you’d like! The full list of commands is found at the bottom of the module. Check to see the required parameters for each function, too.

The Script Module: Commands.psm1


Function InstallDotNet {
    dotnet tool install --global dotnet-ef --version 8.*
    dotnet new tool-manifest
    dotnet nuget add source -n Sitecore https://nuget.sitecore.com/resources/v3/index.json
    dotnet tool install Sitecore.CLI 
}

Function Login {
# Path to the .env file
$envFilePath = "\apps\<APP_NAME_HERE>\.env"

    dotnet sitecore cloud login --client-credentials --client-id "<CLIENT_ID_HERE>" --client-secret "<CLIENT_PASSWORD_HERE>"
}

Function GetEnv {
    param(
            [Parameter(Mandatory=$false)][string]$Env
        )
        $envToLower = $Env.ToLower()
        $environmentId = "<DEV_ENV_ID>"
    if($Env -eq $null -or $Env -eq ""){
            $Env = "dev"
        }

        $envToLower = $Env.ToLower()

        if($envToLower -eq "test" -or $envToLower -eq "tst"){
            $environmentId = "<TEST_ENV_ID>"
        }
        elseif($envToLower -eq "prod" -or $envToLower -eq "prd" -or $envToLower -eq "live"){
            $environmentId = "<PROD_ENV_ID>"
    }

    return $environmentId
}

# Restarts an environment
Function RestartEnv {
    param(
        [Parameter(Mandatory=$false)][string]$Env
    )
    if($Env -eq $null -or $Env -eq ""){
            $Env = "dev"
        }
    $environmentId = GetEnv -Env $Env
    Write-Host "Restarting environment:" -NoNewline
    Write-Host -ForegroundColor Green $Env -NoNewLine
    Write-Host "(" $environmentId ")"
    try{
        Write-Host -ForegroundColor Yellow "Restart initiated! Please wait..."
        dotnet sitecore cloud environment restart --environment-id $environmentId
        Write-Host -ForegroundColor Green "Restart complete!"
    }
    catch {
        Write-Host -ForegroundColor Red "Restart failed. Ensure last deployment was successful before trying again."
    }
    finally {
    }
    
}

# Gets all environment variables on a particular environment.
Function GetEnvVariables {
    param(
        [Parameter(Mandatory=$false)][string]$Env
    )

    if($Env -eq $null -or $Env -eq ""){
        $Env = "dev"
    }

    $envToLower = $Env.ToLower()
    $environmentId = GetEnv -Env $Env

    Write-Host "Getting environment variables for: " -NoNewline
    Write-Host -ForegroundColor Yellow $Env 
    dotnet sitecore cloud environment variable list --environment-id $environmentId
}

Function DownloadTodaysLogs {
    param(
        [Parameter(Mandatory=$false)][string]$Env,
        [Parameter(Mandatory=$false)][string]$Type
    )
    if($Env -eq $null -or $Env -eq ""){
            $Env = "dev"
    }
    if($Type -eq $null -or $Type -eq ""){
            $Type = ""
    }
    $environmentId = GetEnv -Env $Env
    $date = Get-Date -Format "yyyyMMdd"
    Write-Host "Getting logs for" $date

    $allLogs = dotnet sitecore cloud environment log list --environment-id $environmentId
    $allLogsArray = $allLogs.Split("-")
    $todaysLogs = $allLogsArray | Where-Object {$_ -like "*" + $date + "*" -and $_ -like $Type}

    $folderPath = "C:\<PATH_TO_STORE_LOG_FILES>" + $date

    if (-not (Test-Path -Path $folderPath)) {
        New-Item -Path $folderPath -ItemType Directory
        Write-Output "Folder created: $folderPath"
    } else {
        Write-Output "Folder already exists: $folderPath"
    }

    foreach ($log in $todaysLogs) {
        Write-Host "Downloading: " $log
        dotnet sitecore cloud environment log download --logfile $log.Trim() --outputpath $folderPath --environment-id $environmentId
    }

    Write-Host "All logs downloaded for today."

}

# Updates an environment's environment variable based on provided input.
Function UpdateEnvVariable{
    param(
        [Parameter(Mandatory=$false)][string]$Env,
        [Parameter(Mandatory=$true)][string]$Name,
        [Parameter(Mandatory=$true)][string]$Value,
        [Parameter(Mandatory=$false)][string]$Target,
        [Parameter(Mandatory=$false)][string]$Secret
    )

    if($Env -eq $null -or $Env -eq ""){
        $Env = "dev"
    }    

    $envToLower = $Env.ToLower()
    $environmentId = GetEnv -Env $Env
    Write-Host "Updating " -NoNewline
    Write-Host -ForegroundColor Yellow $Env -NoNewline
    Write-Host " environment variable: "  -NoNewline
    Write-Host -ForegroundColor Green $Name -NoNewline
    Write-Host " with value: " -NoNewline
    Write-Host -ForegroundColor Green $Value -NoNewLine
    Write-Host " (Secret:" $Secret ", Target:" $Target ")"
    if($Secret -ne ""){
    Write-Host "Secret included."
        dotnet sitecore cloud environment variable upsert --name $Name --value $Value --target $Target --secret $Secret --environment-id $environmentId
    }
    else{
    Write-Host "Secret not included."
        dotnet sitecore cloud environment variable upsert --name $Name --value $Value --target $Target --environment-id $environmentId
    }
    
}

# Deletes an environment variable
Function DeleteEnvVariable{
    param(
        [Parameter(Mandatory=$false)][string]$Env,
        [Parameter(Mandatory=$true)][string]$Name
    )

    if($Env -eq $null -or $Env -eq ""){
        $Env = "dev"
    }

    $envToLower = $Env.ToLower()
    $environmentId = GetEnv -Env $Env

    Write-Host "Deleting " -NoNewline
    Write-Host -ForegroundColor Yellow $Env -NoNewline
    Write-Host " environment variable: "  -NoNewline
    Write-Host -ForegroundColor Green $Name -NoNewline
    dotnet sitecore cloud environment variable delete --name $Name --environment-id $environmentId
}

# Gets the latest deployment based off of the provided environment.
Function GetLatestDeployment {
    param(
         [Parameter(Mandatory=$false)][string]$Env
    )
    if($Env -eq $null -or $Env -eq ""){
        $Env = "dev"
    }

    $envToLower = $Env.ToLower()
    $environmentId = GetEnv -Env $Env

    Write-Host "Getting latest deployment from:" $Env   

    $deployments = dotnet sitecore cloud deployment list --environment-id $environmentId
        $length = $deployments | Measure-Object -Line
        $latestDeploymentStartLine = $length.Lines - 32
        $totalLines = 31
        $lineCount = 0

        $firstString = "Deployment Id                     : "
        $lastString = "LSOrgClientID"

        $pattern = "$firstString(.*?)$lastString"
        $results = [regex]::Matches($deployments, $pattern)
        $deploymentCount = $results.Count

        $stringArray = @("Started at                        :",
                         "Completed at                      :", 
                         "Provisioning status               :",
                         "Provisioning started at           :",
                         "Provisioning completed at         :",
                         "Provisioning last failure message :",
                         "Build status                      :",
                         "Building started at               :",
                         "Building completed at             :",
                         "Build last failure message        :",
                         "Deployment status                 :",
                         "Deployment started at             :",
                         "Deployment completed at           :",
                         "Deployment last failure message   :",
                         "Post Action status                :",
                         "Post Action started at            :",
                         "Post Action completed at          :",
                         "Post Action last failure message  :",
                         "Calculated status                 :",
                         "Created by                        :"

                         )

        # Get the latest deployment and it's id.
        $latestDeploymentId = $results[$deploymentCount - 1].Value

        ForEach($message in $stringArray){
            $latestDeploymentId = $latestDeploymentId.Replace($message, "`n" + $message)
        }


        Write-Output $latestDeploymentId
}

# Gets the latest deployment from XM Cloud Deploy, and then retrieves its log file.
Function GetLatestDeploymentLog {
param(
         [Parameter(Mandatory=$false)][string]$Env
    )
    if($Env -eq $null -or $Env -eq ""){
        $Env = "dev"
    }

    $envToLower = $Env.ToLower()
    $environmentId = GetEnv -Env $Env

    Write-Host "Getting latest deployment log from:" $Env

    # Get the latest deployment.
    $deployments = dotnet sitecore cloud deployment list --environment-id $environmentId

    $length = $deployments | Measure-Object -Line
    Write-Host $length.Lines
    if($length.Lines -gt 1){
        Write-Host "Found deployment."
        $deploymentId = $length.Lines - 32
        $line = $deployments | Select-Object -Index ($deploymentId)

        $firstString = "Deployment Id                     : "
        $lastString = " Started"

        $pattern = "$firstString(.*?)$lastString"
        $results = [regex]::Matches($deployments, $pattern)
        $deploymentCount = $results.Count

        # Get the latest deployment and it's id.
        $latestDeploymentId = $results[$deploymentCount - 1].Value.Replace($firstString, "").Replace($lastString, "")

        # Retrieve the logs.
        dotnet sitecore cloud deployment log --deployment-id $latestDeploymentId --outputpath "C:\<PATH_TO_STORE_LOG_FILES>"
        
    }
    else{
        Write-Host -ForegroundColor Red "An error has occured. Please try running 'Login' first and then try again."
    }
}


Function PullItems {
    dotnet sitecore ser pull
}

Function PushItems {
    dotnet sitecore ser push
}


Export-ModuleMember -Function GetLatestDeployment
Export-ModuleMember -Function GetLatestDeploymentLog
Export-ModuleMember -Function Login
Export-ModuleMember -Function PullItems
Export-ModuleMember -Function PushItems
Export-ModuleMember -Function UpdateEnvVariable
Export-ModuleMember -Function GetEnvVariables
Export-ModuleMember -Function DeleteEnvVariable
Export-ModuleMember -Function RestartEnv
Export-ModuleMember -Function DownloadTodaysLogs

Leave a comment