Why Can’t we all just get along? AKA Task Sequences vs PowerShell (32/64 bit)
By Steve Bowman
Published December 2, 2016
Estimated Reading Time: 8 minutes

Once Upon A Time….

I was recently in a situation where I needed both 32-bit and 64-bit PowerShell scripts to be able to read/write Task Sequence Variables. After trying various methods I was able to get the 32-bit scripts to read and write Task Sequence Variables successfully.

 

All Was Well Until…

The scenario was an SCCM 2007 client running on a 64-bit Windows 7 device. In this case, the SCCM client is a 32-bit process. As such, the Task Sequence COM object that manages the task sequence variables is a 32-bit object. This is not an issue in cases where you need to run 32-bit Powershell Scripts, but on a 64-bit Operating System, some Powershell CMDlets must be run under a 64-bit Powershell process.

This can be a real problem because you quickly become limited in controlling the flow of the Task Sequence based on results within your 64-bit Scripts.

And They All Lived Happily Ever After…

This bugged me enough that I decided to write a PowerShell Module that would ensure I never dealt with it again. The solution allows you to use Machine Level Environment Variables as “Common Ground” and then return control back to the Task Sequence Variables as needed.

I’ve included the code for the PowerShell module below. Here are a few tips on how to make use of it. I probably don’t need to remind you, but just in case: These scripts will only work if they are run from inside a Task Sequence.

1) Use the “Connect-TSVarsToENV” Cmdlet at the beginning of your Task Sequence to initialize the process of linking the variables. This will create a Machine Level Environment variable for each Task Sequence Variable. The Environment Variables will be prefixed with “TSC_”. So for example, the Task Sequence variable “ScriptRoot” will be copied into an Environment Variable named “TSC_ScriptRoot”.

Note: Keep in mind that the values will not automatically sync without you running an action to do so.

2) Only use the Get-EnvironmentVariable, Get-EnvironmentVariables, and Set-EnvironmentVariable cmdlets to manage Environment Variables. The reason for this is that the build in powershell ENV: Drive only updates when PowerShell is started, so any changes made will be invisible to active Powershell Scripts. The Cmdlets I’ve provided use the .NET object to access the Environment Variables directly, so you get the correct values each time.

3) Use the “Sync-TSENVars” to copy all variables one way or the other as needed.

4) Use the “Disconnect-TSVarsFromENV” Cmdlet to clean up by copying all Environment values back into the Task Sequence Object before Removing the Environment copies.

5) The “Compare-TSVarsToENV” Cmdlet will return a table showing the values for both copies of your TS Variables. It also indicates which variables do not currently match on one side or the other.

I hope you find these as useful as I did.   If you find another creative use for them or have a question, let me know at todd.linke@model-techology.com.

Now as promised, here is the source code:

Function Set-EnvironmentVariable {

 

 param(
 [parameter(Mandatory=$true)][string]$VarName,
 [parameter(Mandatory=$true)][string]$VarValue,
 [ValidateSet("User","Machine")][string]$VarType = "Machine",
 [string]$VarNamePrefix = ""
 )

 $VariableName = "$($VarNamePrefix)$($VarName)"
 [Environment]::SetEnvironmentVariable("$VariableName","$VarValue","$VarType")
 #$VarValue = [Environment]::GetEnvironmentVariable("$VariableName","$VarType")
 #Write-Verbose "$VariableName is now set to $VarValue"
}

Function Get-EnvironmentVariable {

 <#
 .SYNOPSIS
 Gets the value of an environment variable.
 .DESCRIPTION
 This script is used to get the value of a windows environment variable.
 It can get either Machine or User level variables, based on parameters used.
 If the variable does not exist, no value will be returned.
 
 .EXAMPLE
 The following example gets the value of the "Temp" environment variable 
 in the machine context:

 Get-EnvironmentVariable -VarName "Temp" -Machine
 
 .EXAMPLE
 The following example gets the value of the "Temp" environment variable in the current 
 user context:

 Get-EnvironmentVariable -VarName "Temp" -User

 .EXAMPLE
 The following example gets the value of the "Temp" environment variable 
 in the machine context:

 Get-EnvironmentVariable -VarName "Temp"

 .PARAMETER VarName
 The name of the environment variable to get.
 
 .PARAMETER VarType
 The context the variable should be read from. 
 Valid values are either "Machine" or "User".
 If not specified, "Machine" will be assumed.
 
 .NOTES
 This script was written by Todd Linke, a Senior Consultant at Model Technology.
 Last Updated on 12/2/2016

 #>


 param(
 [parameter(Mandatory=$true)][string]$VarName,
 [ValidateSet("User","Machine")][string]$VarType = "Machine"
 )
 $VarValue = [Environment]::GetEnvironmentVariable("$VarName","$VarType")
 
 Write-Output $VarValue

}

Function Get-EnvironmentVariables {

 <#
 .SYNOPSIS
 Gets the value of all environment variables in a specific scope.
 .DESCRIPTION
 This script is used to get the values of all windows environment variables.
 It can get either Machine or User level variables, based on parameters used.
 
 .EXAMPLE
 The following example gets the value of all environment variables
 in the machine context:

 Get-EnvironmentVariables -Machine
 
 .EXAMPLE
 The following example gets the values of all environment variables in the current 
 user context:

 Get-EnvironmentVariables -User

 .EXAMPLE
 The following example gets the values of all environment variables 
 in the machine context:

 Get-EnvironmentVariables

 .PARAMETER VarType
 The context the variables should be read from. 
 Valid values are either "Machine" or "User".
 If not specified, "Machine" will be assumed.
 
 .NOTES
 This script was written by Todd Linke, a Senior Consultant at Model Technology.
 Last Updated on 12/2/2016

 #>

 param(
 [ValidateSet("User","Machine")][string]$VarType = "Machine"
 )
 $VarValues = [Environment]::GetEnvironmentVariables("$VarType")
 
 Write-Output $VarValues

}

Function Get-TaskSequenceVariable {

 <#
 .SYNOPSIS
 Gets the value of a variable within the current task sequence.
 
 .DESCRIPTION
 This script is used to get the value of a task sequence variable.
 If the variable does not exist, no value will be returned.
 
 .EXAMPLE
 The following example gets the value of the "Temp" 
 task sequence variable:

 Get-TaskSequenceVariable -VarName "Temp"

 .PARAMETER VarName
 The name of the task sequence variable to get.
 
 .NOTES
 This script was written by Todd Linke, a Senior Consultant at Model Technology.
 Last Updated on 12/2/2016

 #>

 param(
 [parameter(Mandatory=$true)][string]$VarName 
 ) 

Try
{
$tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment -Erroraction Stop
}
Catch
{
Write-Error -Message "Task Sequence Environment object could not be found." -RecommendedAction "Verify you are running this script inside a Task Sequence"
Break
}

$VarValue = $tsenv.Value("$VarName")
Write-Output $VarValue


}

Function Set-TaskSequenceVariable {
 <#
 .SYNOPSIS
 Sets the value of a variable within the current task sequence.
 
 .DESCRIPTION
 This script is used to set the value of a task sequence variable.
 If the variable does not exist, it will be created.
 
 .EXAMPLE
 The following example sets the value of the "Temp" 
 task sequence variable:

 Set-TaskSequenceVariable -VarName "Temp" -VarValue "C:\Minint\Temp"

 .EXAMPLE
 The following example sets the value of the "OSD_Temp" 
 task sequence variable:

 Set-TaskSequenceVariable -VarName "Temp" -VarValue "C:\Minint\Temp" -VarNamePrefix "OSD_"

 .PARAMETER VarName
 The name of the task sequence variable to set.

 .PARAMETER VarValue
 The value to be written in the variable.

 .PARAMETER VarNamePrefix
 A text string to be appended to the beginning of the variable name.
 
 .NOTES
 This script was written by Todd Linke, a Senior Consultant at Model Technology.
 Last Updated on 12/2/2016

 #>
 
 param(
 [parameter(Mandatory=$true)][string]$VarName,
 [parameter(Mandatory=$true)][string]$VarValue,
 [string]$VarNamePrefix = "" 
 ) 

 Try
 {
 $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment -Erroraction Stop
 }
 Catch
 {
 Write-Error -Message "Task Sequence Environment object could not be found." -RecommendedAction "Verify you are running this script inside a Task Sequence"
 Break
 }

 $VariableName = "$($VarNamePrefix)$($VarName)"
 $tsenv.Value("$VarName") = $VarValue
 Write-Verbose "$VariableName is now set to $VarValue"


}

Function Get-TaskSequenceVariables {

 <#
 .SYNOPSIS
 Gets the values of all variables within the current task sequence.
 
 .DESCRIPTION
 This script is used to get the values of all task sequence variables.
 
 .EXAMPLE
 The following example gets the value of all
 task sequence variables:

 Get-TaskSequenceVariables
 
 .NOTES
 This script was written by Todd Linke, a Senior Consultant at Model Technology.
 Last Updated on 12/2/2016

 #>


 Try
 {
 $tsenv = New-Object -COMObject Microsoft.SMS.TSEnvironment -Erroraction Stop
 }
 Catch
 {
 Write-Error -Message "Task Sequence Environment object could not be found." -RecommendedAction "Verify you are running this script inside a Task Sequence"
 Break
 }
 
 $TSVars = @()
 
 Foreach ($item in $Tsenv.GetVariables()){

 $Var = New-Object -TypeName PSObject
 $Var | Add-Member -MemberType NoteProperty -Name "Name" -Value "$item"
 $Var | Add-Member -MemberType NoteProperty -Name "Value" -Value "$($TSEnv.Value($item))"
 $TSVars += $Var

 }

 Write-Output $TSVars

}

Function Convert-TStoENV {
 
 <#
 .SYNOPSIS
 Copies all variables within the current task sequence to Environment Variables.
 
 .DESCRIPTION
 This script is used to copy all variables within the current task sequence to Environment Variables.
 The Environment Variable names will be created with a "TSC_" prefix.
 If the Environment Variables already exist, they will be overwritten.
 
 .EXAMPLE
 The following example copies all task sequence variables to Environment Variables.

 Convert-TStoENV
 
 .NOTES
 This script was written by Todd Linke, a Senior Consultant at Model Technology.
 Last Updated on 12/2/2016

 #>

 $TSVars = Get-TaskSequenceVariables
 Foreach ($TSVar in $TSVars) {
 Write-Verbose "Setting Environment variable for $TSVar.Name"
 $Name = $TSVar.Name
 $Value = $TSVar.Value
 Set-EnvironmentVariable -VarName "TSC_$Name" -VarValue "$Value" -VarType Machine
 }
}

Function Convert-ENVtoTS {

 <#
 .SYNOPSIS
 Copies all Environment Variables starting with "TSC_" into Task Sequence Variables.
 
 .DESCRIPTION
 This script is used to copy all Environment Variables to variables within the current task sequence.
 The Environment Variables will only be copied if they begin with a "TSC_" prefix.
 If the Task Sequence Variables already exist, they will be overwritten.
 Task Sequence Variables that are read-only (Name begins with an underscore) are not 
 updated.
 
 .EXAMPLE
 The following example copies all task sequence variables to Environment Variables.

 Convert-ENVtoTS
 
 .NOTES
 This script was written by Todd Linke, a Senior Consultant at Model Technology.
 Last Updated on 12/2/2016

 #>


 $ENVars = [Environment]::GetEnvironmentVariables("Machine")

 Foreach ($Key in $ENVars.Keys) {
 If ($Key -like "TSC_*") {
 $Name = $Key.Replace("TSC_","")
 $Value = "$($ENVars.$Key)"
 If ($Name -notlike "_*" -and $Name.Length -gt 0) {
 #Write-Output "Setting $Name"
 Set-TaskSequenceVariable -VarName "$Name" -VarValue "$Value"
 }
 }
 }
}

Function Clean-TSVarsFromENV {

<#
 .SYNOPSIS
 Removes all Environment Variables starting with "TSC_".
 
 .DESCRIPTION
 This script is used to remove all Environment Variables if they begin with a "TSC_" prefix.
 
 .EXAMPLE
 The following example removes all Environment Variables with a name starting with "TSC_".

 Clean-TSVarsFromENV
 
 .NOTES
 This script was written by Todd Linke, a Senior Consultant at Model Technology.
 Last Updated on 12/2/2016

 #>

 $ENVars = [Environment]::GetEnvironmentVariables("Machine")

 Foreach ($Key in $ENVars.Keys) {
 If ($Key -like "TSC_*") {
 [Environment]::SetEnvironmentVariable("$Key","","Machine")
 
 }
 }

}

Function Connect-TSVarsToENV {

<#
 .SYNOPSIS
 Initializes the process of using environment variables in place of task sequence variables.
 
 .DESCRIPTION
 This script is used to do the initial copy of task Sequence Variables into Environment Variables
 that are prefixed by "TSC_".
 
 .EXAMPLE
 The following example creates Environment Variable copies of all Task Sequence Variables. 
 The Environment Variable names are prefixed by "TSC_".

 Connect-TSVarsToENV
 
 .NOTES
 This script was written by Todd Linke, a Senior Consultant at Model Technology.
 Last Updated on 12/2/2016

 #>




 Convert-TStoENV

}

Function Disconnect-TSVarsFromENV {

<#
 .SYNOPSIS
 Ends the process of using environment variables in place of task sequence variables.
 
 .DESCRIPTION
 This script is used to update task Sequence Variables with the values that are currently 
 in the corresponding Environment Variables (prefixed by "TSC_"). Once updated, the Environment
 Variables are removed.
 
 .EXAMPLE
 The following example cleans up up task Sequence Variables with the values that are currently 
 in the corresponding Environment Variables (prefixed by "TSC_"), and then deletes the Environment 
 Variables.

 Disconnect-TSVarsFromENV
 
 .NOTES
 This script was written by Todd Linke, a Senior Consultant at Model Technology.
 Last Updated on 12/2/2016

 #>



 Convert-ENVtoTS
 Clean-TSVarsFromENV

}

Function Compare-TSVarsToENV {

<#
 .SYNOPSIS
 Compares task sequence variables to environment variables of the same name that are
 prefixed by "TSC_".
 
 .DESCRIPTION
 Compares task sequence variables to environment variables of the same name that are
 prefixed by "TSC_". It can return only same, different, or all values.
 
 .EXAMPLE
 The following example returns all task Sequence Variables and their
 corresponding Environment Variables (prefixed by "TSC_").

 Compare-TSVarsToENV -ResultSet All
 
 .EXAMPLE
 The following example returns only task Sequence Variables and their
 corresponding Environment Variables (prefixed by "TSC_")
 where the values do not match.

 Compare-TSVarsToENV -ResultSet Different

 .EXAMPLE
 The following example returns only task Sequence Variables and their
 corresponding Environment Variables (prefixed by "TSC_")
 where the values match.

 Compare-TSVarsToENV -ResultSet Same

 .PARAMETER ResultSet
 Indicates the match status(es) that should be returned. 
 Valid values are "Same", "Different', "All"

 .NOTES
 This script was written by Todd Linke, a Senior Consultant at Model Technology.
 Last Updated on 12/2/2016

 #>


 param (
 [ValidateSet("Same","Different","All")][String]$ResultSet
 )

 $TSVars = Get-TaskSequenceVariables
 $ENVars = Get-EnvironmentVariables


 $CompareTable = @()

 Foreach ($TSVar in $TSVars) {

 $VarName = $($TSVar.Name)
 $VarValue = $($TSVar.Value)
 $EnVarName = "TSC_$VarName"
 $EnVarValue = $ENVars["$ENVarName"]

 If ($EnVarValue.Length -eq 0) {
 $EnvarName = ""
 }
 
 $CompVar = New-Object -TypeName PSObject 
 $CompVar | Add-Member -MemberType NoteProperty -Name "TSVarName" -Value "$VarName"
 $CompVar | Add-Member -MemberType NoteProperty -Name "TSValue" -Value "$VarValue"
 $CompVar | Add-Member -MemberType NoteProperty -Name "ENVarName" -Value "$EnVarName"
 $CompVar | Add-Member -MemberType NoteProperty -Name "EnValue" -Value "$EnVarValue"
 
 If ($VarValue -eq $EnVarValue) {
 $CompareResult = "Same"
 }
 Else {
 $CompareResult = "Different"
 }
 $CompVar | Add-Member -MemberType NoteProperty -Name "Status" -Value $CompareResult

 $CompareTable += $CompVar


 }


 If ($ResultSet -ne "All") {
 $CompareTable = $CompareTable | Where-Object {$_.Status -eq $ResultSet}
 }

 Write-Output $CompareTable

}

Function Sync-TSENVars {

<#
 .SYNOPSIS
 Copies all Environment Variables starting with "TSC_" into Task Sequence Variables or
 all Task Sequence Variables to their associated Environment Variables.
 
 .DESCRIPTION
 This script copies all Environment Variables starting with "TSC_" into Task Sequence Variables or
 all Task Sequence Variables to their associated Environment Variables.
 If a source variable does not have a match, one will be created.
 
 .EXAMPLE
 The following example copies all Environment Variables starting with "TSC_" into Task Sequence Variables.

 Sync-TSENVars -Source ENV
 
 .EXAMPLE
 The following example copies all Task Sequence Variables into Environment Variables starting with "TSC_".

 Sync-TSENVars -Source TS
 
 .PARAMETER Source
 Indicates the set of variables that values should be copied from.
 
 .NOTES
 This script was written by Todd Linke, a Senior Consultant at Model Technology.
 Last Updated on 12/2/2016

 #>



 param(
 [parameter(Mandatory=$true)][ValidateSet("TS","ENV")][string]$Source
 )

 If ($Source -eq "TS") {Convert-TStoENV}
 elseif ($Source -eq "ENV") {Convert-ENVtoTS}
 else {
 #No Action
 }
}
Article By Steve Bowman
Steve Bowman is a Partner at Model Technology as well as their Vice President of Sales and Marketing. Steve is a father, husband, Franciscan, and lover of technology. He's bilingual in business and technology and have over 30 years of experience in selling enterprise technology solutions in a variety of industries.

Related Posts