Skip to content

Redstone Class

Raymond Piller edited this page Mar 15, 2023 · 8 revisions

The Redstone class is a helper class that makes some things a little easier in a way that only a class object can do. None of the functions in PSRedstone rely on the class itself. However, using the class can augment functionality. You just need to provide a settings.json and a Redstone Block ( ͡° ͜ʖ ͡°) at the top of your script to use the module:

Instantiating Redstone

Redstone can be instantiated in one of three ways:

  • Without parameters: New-Redstone
  • With a parameter: New-Redstone $fullPathToSettingsJson; where the parameter can be cast as [IO.FileInfo], so a UNC path will not work.
  • With parameters: New-Redstone $Publisher $Product $Version $Action; where all of those parameters are [string]s.

If Redstone is instantiated with no parameters, a settings.json must exist. The parameters and data in the settings.json are just used to make some decisions easier.

The Settings JSON File

Redstone will look for the case-insensitive JSON file named settings.json file in the following order:

  1. In the current working directory using $PWD.
  2. In the same directory where the executing script is; the $PSScriptRoot of the file that instantiated Redstone.
  3. In the parent of the current working directory using $PWD.
  4. In the parent of the directory where the executing script is; the $PSScriptRoot of the file that instantiated Redstone.

If a settings.json is provided Redstone will import the data to $Redstone.Settings.JSON.Data. To make the data more accessible, Redstone will output the JSON data as the second item of the returned array. That's why we suggest setting up the Redstone Block the way we have.

Sample JSON

At a minimum, the following structure is required:

{
    "Publisher": "Acme",
    "Product": "Rocket",
    "Version": "1.2.3"
}

Assuming your Redstone Block was set up the same as our example above, Publisher should be accessed via:

  • $redstone.Settings.JSON.Data.Publisher
  • $settings.Publisher

ℹ: If you instantiate with parameters, the $settings variable will be empty.

Anything else in the settings.json is arbitrary and is purely for use in your scripts. See the Quick Start for an example.

Exploring Redstone Class

This exploration assumes that you've already installed PSRedstone. For this article, we'll have an installer for Acme Rocket 1.2.3 that we're working on. Here's our settings.json:

{
    "Publisher": "Acme",
    "Product": "Rocket",
    "Version": "1.2.3",
    "Installer": {
        "UserName": "VertigoRay",
        "CompanyName": "PSRedstone & Co.",
        "SerialNumber": "bfa7409e-485c-45cf-bd42-1652c2c84e17"
    }
}

This is the start of our install script, install.ps1:

#region Redstone Block
#Requires -Modules PSRedstone
$redstone, $settings = New-Redstone
#endregion Redstone Block

Env

Redstone Env contains calculated versions of environment variables. It gives you what you likely want from the environment variable without worrying if the process you're in is 32 or 64 bit or if the OS is 32 or 64 bit. Here are values for each situation:

  • 64-bit process:
    • CommonProgramFiles: C:\Program Files\Common Files
    • CommonProgramFiles(x86): C:\Program Files (x86)\Common Files
    • PROCESSOR_ARCHITECTURE: AMD64
    • ProgramFiles: C:\Program Files
    • ProgramFiles(x86): C:\Program Files (x86)
    • System32: C:\WINDOWS\System32
    • SysWOW64: C:\WINDOWS\SysWOW64
  • 32-bit process on 64-bit OS:
    • CommonProgramFiles: C:\Program Files\Common Files
    • CommonProgramFiles(x86): C:\Program Files (x86)\Common Files
    • PROCESSOR_ARCHITECTURE: AMD64
    • ProgramFiles: C:\Program Files
    • ProgramFiles(x86): C:\Program Files (x86)
    • System32: C:\WINDOWS\SysNative
    • SysWOW64: C:\WINDOWS\SysWOW64
  • 32-bit OS:
    • CommonProgramFiles: C:\Program Files\Common Files
    • CommonProgramFiles(x86): C:\Program Files\Common Files
    • PROCESSOR_ARCHITECTURE: x86
    • ProgramFiles: C:\Program Files
    • ProgramFiles(x86): C:\Program Files
    • System32: C:\WINDOWS\System32
    • SysWOW64: C:\WINDOWS\System32

This saves you from having to do an if statement like this:

if ([System.Environment]::Is64BitOperatingSystem) {
    # x64 OS
    if ([System.Environment]::Is64BitProcess) {
        # x64 Process
        $arch = $env:PROCESSOR_ARCHITECTURE
    } else {
        # Running as x86 on x64 OS
        $arch = $env:PROCESSOR_ARCHITEW6432
    }
} else {
    # x86 OS
    $arch = $env:PROCESSOR_ARCHITECTURE
}

If you want the process/OS dependent values, just use $env:.

OS

PS > $redstone.OS

Name                           Value
----                           -----
IsServerOS                     False
MachineDNSDomain
ProductType                    1
Name                           Microsoft Windows 10 Enterprise|C:\Windows|\Device\Harddisk0\Partition2
IsWorkStationOS                True
MachineWorkgroup               WORKGROUP
Is64BitProcess                 True
IsDomainControllerOS           False
LogonServer
ServicePack
ProductTypeName                Workstation
MachineDomainController
Version                        10.0.19041.0
UserDomain                     WORKGROUP
IsMachinePartOfDomain          False
MachineADDomain
ShorterName                    Windows 10
Is64BitOperatingSystem         True
Is64BitProcessor               True
ShortName                      Windows 10 Enterprise
UserDNSDomain

ProfileList

PS > $redstone.ProfileList

Name                           Value
----                           -----
ProfilesDirectory              C:\Users
ProgramData                    C:\ProgramData
Profiles                       {System.Collections.Hashtable, System.Collections.Hashtable, System.Collections.Hashtable, System.Collections.Hashtable...}
Default                        C:\Users\Default
Public                         C:\Users\Public


PS > $redstone.ProfileList.Profiles

Name                           Value
----                           -----
Username                       SYSTEM
Path                           C:\Windows\system32\config\systemprofile
Domain                         NT AUTHORITY
SID                            S-1-5-18
Username                       LOCAL SERVICE
Path                           C:\Windows\ServiceProfiles\LocalService
Domain                         NT AUTHORITY
SID                            S-1-5-19
Username                       NETWORK SERVICE
Path                           C:\Windows\ServiceProfiles\NetworkService
Domain                         NT AUTHORITY
SID                            S-1-5-20
Username                       WDAGUtilityAccount
Path                           C:\Users\WDAGUtilityAccount
Domain                         A6FB6907-F9CA-4
SID                            S-1-5-21-2047949552-857980807-821054962-504
Username                       ContainerAdministrator
Path                           C:\Users\ContainerAdministrator
Domain                         User Manager
SID                            S-1-5-93-2-1
Username                       ContainerUser
Path                           C:\Users\ContainerUser
Domain                         User Manager
SID                            S-1-5-93-2-2

Settings

If an override is allowed via a Registry value, the same override can be done via an environment variable. Just prepend the environment variable with PSRedstone.

For example:

Override KeyRoot:

  • Registry: RegistryKeyRoot
  • Environment: PSRedstoneRegistryKeyRoot

Registry

This setting stores important registry keys. All of these, including the KeyRoot, can be overridden in the registry. All of these can be overridden with an environment variable. Overrides via an environment variable take precedence over the registry.

KeyRoot

  • Default Value: HKEY_LOCAL_MACHINE\SOFTWARE\com.github.VertigoRay\PSRedstone
  • Override Environment Variable: PSRedstoneRegistryKeyRoot

The location where PSRedstone core settings are configured. If you override this setting, then it affects where all other settings are pulled from. If nothing is overridden here than all default configurations are used. You can see all overriddable settings by exploring the Redstone class.

Vars

Redstone Vars (aka PrivateVars in a former life) contains organizational-specific, publisher-specific, and product-specific variables that you define in the Windows registry. This is hierachal meaning that the settings applied in the order mentioned in the previous sentence. If a settings with the same name is applied in both Org and Product, then the Product one is used. Don't worry, you can still access all of the Vars in $redstone.Settings.

There's a fundamental flaw that I haven't addressed yet. If there's a value and sub-key with the same name at the same key level, the sub-key won't be processed.

There's a GetVar function that is purely designed to make things easier when getting a Var using a path in string form. It has the added benefit of returning a provided default value if the path doesn't exist.

Here's an example Vars structure:

$redstone.Vars = @{
    Thing1 = 1
    Thing2 = @{
        This1 = 21
        This2 = @{
            That1 = 221
            That2 = 222
            That3 = 223
            That4 = $null
        }
        This3 = 23
    }
    Thing3 = 3

Here's some examples of pulling data out of Vars:

$redstone.Vars.Thing1
# Returns: 1

$redstone.Vars.Thing2.This2.That1
# Returns: 221

$redstone.Vars.Thing2.This4
# Returns: $null

$redstone.Vars.Thing2.This2.That4
# Returns: $null

$redstone.GetVar('Thing1')
# Returns: 1

$redstone.GetVar('Thing2.This2.That1')
# Returns: 221

$redstone.GetVar('Thing2.This4')
# Returns: $null

$redstone.GetVar('Thing2.This2.That4')
# Returns: $null

$redstone.GetVar('Thing2.This4', 'nope')
# Returns: 'nope'

$redstone.GetVar('Thing2.This2.That4', 'nope')
# Returns: $null

# GetVar() is just shorthand for Get-RedstoneHashtableValue:
Get-RedstoneHashtableValue -Hashtable $redstone.Vars -Path 'Thing2.This2.That4' -Default 'nope'
# Returns: $null
What if I want to use GetVar to access a registry value with a dot?

Don't use GetVar.

$valueWithDots = $redstone.Vars.'Value.With.Dots'

If you're wanting to use the default value feature, you'll just have to write the logic yourself:

$valueWithDots = if ($redstone.Vars.Keys -contains 'Value.With.Dots') {
   $redstone.Vars.'Value.With.Dots'
} else {
   'Default Value'
}

Org

  • Default Value: HKEY_LOCAL_MACHINE\SOFTWARE\com.github.VertigoRay\PSRedstone\Org
    • If KeyRoot has been overridden than this becomes ${KeyRoot}\Org.
  • Override: RegistryKeyOrg
  • Recurse: $true
    • Override: Key RegistryKeyOrgRecurse

I expect Org to be a relatively simple/flat key. While there may be more things there, in my org I only care about the root of that key.

  • HKEY_LOCAL_MACHINE\SOFTWARE\MyOrg

However, I didn't want to force my design decisions on you, so I gave you the ability to override recursiveness.

Publisher

  • Default Value: HKEY_LOCAL_MACHINE\SOFTWARE\com.github.VertigoRay\PSRedstone\${Publisher}
  • Override: RegistryKeyPublisherParent
  • Recurse: $false
    • Override: RegistryKeyPublisherRecurse

I expect Publisher to be a relatively simple/flat key. While there may be more things there, on my endpoints the subkeys are the products.

The registry keys may look something like this for Microsoft and VMware:

  • HKEY_LOCAL_MACHINE\SOFTWARE\MyOrg\Software\
    • Microsoft
    • VMware

However, I didn't want to force my design decisions on you, so I gave you the ability to override recursiveness.

Product

  • Default Value: HKEY_LOCAL_MACHINE\SOFTWARE\com.github.VertigoRay\PSRedstone\${Publisher}\${Product}
    • The $Publisher and $Product are defined in settings or during instantiation.
    • If KeyRoot has been overridden than this becomes ${KeyRoot}\${Publisher}\${Product}.
  • Override: RegistryKeyProductParent
  • Recurse: $true
    • Override: RegistryKeyProductRecurse

I expect Product to be the deepest key. So, if you have sub-keys here you likely want them.

The registry keys may look something like this for Microsoft VS Code and VMware Horizon Client:

  • HKEY_LOCAL_MACHINE\SOFTWARE\MyOrg\Software\
    • Microsoft
      • VS Code
    • VMware
      • Horizon Client

However, I didn't want to force my design decisions on you, so I gave you the ability to override recursiveness.