Embedding resources in PowerShell scripts

A challenge I’ve come across a number of times while working with PowerShell scripts for Sitecore is that scripts sometimes have other files they depend on. Making sure that your scripts and their dependencies stay in sync can be a challenge at times, especally when scripts get reused across an organisation. So what approaches can you use for this?

Central storage

Probably the most common approach to this problem is to put your dependencies in some sort of central storage. You might have a file share or a web server which the dependencies get put on. This can work one of a number of ways:

The lowest effort approach is for the script user to download bot the script and the dependencies at the same time. This is zero effort for you as the maintainer of the script, but it relies on the user doing the right thing. They might forget about dependencies and get “not found” errors, or they might accidentally end up with a different version of the script and the dependences – which may lead to much more subtle bugs.

Alternatively you might choose to make the script download its own dependencies as it executes. This gets rid of the two issues above, but it potentially causes other problems. Maybe the user runs the script in a low-privelidge environment and it fails to be able to save the downloads. Or perhaps the user doesn’t have a valid network route to the central storage when they run the scripts and the download itself fails.

Having been bitten by both of these issues in the past, I was considering whether a third approach might be workable:

Embedded resources

If the assets could be bundled inside the script, many of these issues could be avoided. That’s easy if your asset is a simple bit of text – you could just have a literal string. PowerShell’s multi-line string literals (sometimes referred to as “here-strings”) work well for this:

$text = @"
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <sc.variable name="dataFolder">
      <patch:attribute name="value">C:\inetpub\wwwroot\sc82u2\Data</patch:attribute>
    </sc.variable>
  </sitecore>
</configuration>
"@

But what if you needed an asset that wasn’t text? Well how about encoding it as base-64 text? That can represent anything:

function Base64Encode-File
{
  param
  (
    [Parameter(Mandatory = $true)]
    [string]$file
  )
  process
  {
    $c = Get-Content $file -Encoding Byte 
    return [System.Convert]::ToBase64String($c)
  }
}

Now you have something that is a chunk of text no matter what the source file was, and you can drop it into a script:

$asset = "//5OAG8AcABlACwAIABzAG8AcgByAHkAIAAtACAASQAgAGQAaQBkAG4AJwB0ACAAcAB1AHQAIABhAG4AeQB0AGgAaQBuAGcAIABpAG4AdABlAHIAZQBzAHQAaQBuAGcAIABpAG4AIABoAGUAcgBlACAAOwAtACkADQAKAA=="

It’s easy enoygh to turn that data back into the original asset. You might want to write it to disk (making sure the folder you’re writing to exists as well):

function Write-EmbeddedFile
{
  param
  (
    [string]$base64,
    [string]$targetFile
  )
  process
  {
    $Content = [System.Convert]::FromBase64String($base64)

    $folder = Split-Path $targetFile
    if(!(Test-Path $folder))
    {
      New-Item $folder -ItemType Directory | Out-Null
    }

    Set-Content -Path $targetFile -Value $Content -Encoding Byte
  }
}

Or potentially you could make use of it in-memory if your scenario suited that…

One thought on “Embedding resources in PowerShell scripts

  1. Pingback: A little PowerShell hack for sending files to a remote machine | Jeremy Davis

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.