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
}
}