How two concatenate environment variables names in powershell - powershell

Say I have the following environment variables:
a = Poke
b = mon
Pokemon= Feraligatr
I want to be able to concatenate a and b environment variables to get the variable name Pokemon and the get Pokemon value like $($env:ab) or $($env:$($env:a)$($env:b)) (This examples does not work)

Building on the helpful comments:
You're looking for indirection, i.e. the ability to refer to an environment variable indirectly, via another (environment) variable(s) storing the target variable's name.
PowerShell-idiomatic solution:
Use the Env: drive in combination with the Get-Content cmdlet:
# The target environment variable.
$env:Pokemon='bingo!'
# The variables that, in combination, return the *name*
# of the target environment variable.
$env:a = 'Poke'
$env:b = 'mon'
# Use Get-Content and the env: drive to retrieve
# an environment variable by an *indirectly* specified name.
# Note:
# * env:$env:a$env:b is treated like "env:$env:a$env:b",
# i.e. an expandable (interpolating string).
# * For better visual delineation of the variables, use:
# env:${env:a}${env:b}
# * `-ErrorAction Ignore` ignores the case when the target var.
# doesn't exist (quietly returns $null`)
# -> 'bingo!'
Get-Content -ErrorAction Ignore env:$env:a$env:b
# Alternative, with explicit string concatenation.
Get-Content -ErrorAction Ignore ('env:' + $env:a + $env:b)
Note:
To set environment variables indirectly, use the Set-Content cmdlet; e.g.:
$varName = 'FOO'
Set-Content env:$varName BAR # $env:FOO now contains 'BAR'
Applying the same technique to regular shell variables (non-environment variables), requires either use of the variable: drive, or, for more flexibility, the Get-Variable and Set-Variable cmdlets - see this answer.
More more information about expandable (interpolating) string literals such as "env:$env:a$env:b", see the conceptual about_Quoting help topic.
.NET API alternative:
As Max points out, you can also use the static System.Environment.GetEnvironmentVariable .NET method:
[Environment]::GetEnvironmentVariable("${env:a}${env:b}")
For more information about calling .NET API methods from PowerShell, see the conceptual about_Methods help topic.

Related

Is the following a scoping issue when trying to use Tee-Object

Environment:
Windows Server 2016
Windows 10 Pro
PowerShell 5.1
$myVariable is empty, I think and I'm expecting there to be a string value.
Invoke-Command -ComputerName WKSP000D1E3F -Credential $creds -ScriptBlock {
sqlcmd -E -Q "select top 1 FirstName from customers" -d database1 -S "(localdb)\ProjectsV13" | Tee-Object -Variable myVariable
}
Write-Host $myVariable
Cpt.Whale has provided the crucial pointer in a comment: you fundamentally cannot set local variables from a script block being executed remotely (via Invoke-Command -ComputerName) - you must use output from the script block to communicate data back to the caller.
While you could apply Tee-Object locally instead (Invoke-Command ... | Tee-Object), there's a simpler solution, which works with all cmdlets, including cmdlet-like (advanced) functions and scripts:
Use the common -OutVariable (-ov) parameter to capture a cmdlet's output in a self-chosen variable while passing that output through:
# Note the `-OutVariable myVariable` part
# and that the variable name must be specified *without* a leading "$"
# Output is still being passed through.
Invoke-Command -OutVariable myVariable -ComputerName WKSP000D1E3F -Credential $creds -ScriptBlock {
sqlcmd -E -Q "select top 1 FirstName from customers" -d database1 -S "(localdb)\ProjectsV13"
}
# $myVariable now contains the captured content.
By contrast, if you want to capture output only, without also passing it through (to the display, by default), you can heed Santiago Squarzon's advice and simply assign the Invoke-Command call to your variable ($myVariable = Invoke-Command ...).
Notes re -OutVariable(-ov):
As shown above, and as shown with Tee-Object -Variable in your question, the name of the self-chosen target variable must be specified without a leading $, e.g. -OutVariable var, not Out-Variable $var; if you did the latter, the value of a preexisting $var variable (if defined) would be used as the variable name.
Unlike directly captured output, the target variable always receives array(-like) data, specifically, an instance of the System.Collections.ArrayList class - even if only one output object is present; e.g.:
# -> 'types: v1: String vs. v2: ArrayList'
$v1 = Write-Output -OutVariable v2 'one'
"types: v1: $($v1.GetType().Name) vs. v2: $($v2.GetType().Name)"
That is, while directly capturing output captures a single output object as-is, and multiple ones in a regular PowerShell array (of type [object[]], -OutVariable always creates an ArrayList - see GitHub issue #3154 for a discussion of this inconsistency.
With commands that do not support -OutVariable, namely simple scripts and functions as well as external programs:
To pass the output through in streaming fashion, i.e. as it becomes available, pipe to Tee-Object -Variable; e.g.:
# Passes output through as it is being emitted.
some.exe | Tee-Object -Variable myVariable
Otherwise - i.e. if it is acceptable to collect all output first, before passing it through - simply enclose an assignment statement in (...) to pass its value through - this approach performs better than Tee-Object -Variable; e.g.:
# Collects all output first, then passes it through.
($myVariable = some.exe)

How to retrieve original, unresolved (unexpanded) registry value?

In the System Properties > Environment Variables > User variables > PATH contains:
%USERPROFILE%\Downloads\SysinternalsSuite;%USERPROFILE%\bin
The value can be retrieved with:
PS C:\src\t> (Get-ItemProperty -Path HKCU:\Environment).PATH
C:\Users\lit\Downloads\SysinternalsSuite;C:\Users\lit\bin
Is there any way to get the original value without variable expansion? It almost seems like Get-ItemProperty needs a -Raw switch.
PetSerAl, as many times before, has provided an effective solution in a terse comment on the question.
Indeed, PowerShell's Get-ItemProperty / Get-ItemPropertyValue cmdlets currently (PowerShell 7.3.0) lack the ability to retrieve a REG_EXPAND_SZ registry value's raw value, meaning the value as stored in the registry before the embedded environment-variable references (e.g., %USERPROFILE%) are expanded (interpolated).
Direct use of the .NET API is therefore needed:
(Get-Item -Path HKCU:\Environment).GetValue(
'PATH', # the registry-value name
$null, # the default value to return if no such value exists.
'DoNotExpandEnvironmentNames' # the option that suppresses expansion
)
See [Microsoft.Win32.RegistryKey].GetValue().
Note: 'DoNotExpandEnvironmentNames' is automatically converted to [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentVariables by PowerShell; you may use the latter as well.

Powershell - Why does $ENV | GET-MEMBER not work?

I come from a C, C++, C#, python background so i'm applying this thought pattern to Powershell which i'm learning from scratch but I'm a little confused so far as at first glance it seems to be inconsistent and does not follow a fixed base class type structure for all objects so that things can be queried in a consistent manner.
The following works fine:
$host | get-member
$env:username
So $env is a valid object but this does not work:
$env | get-member
These also do not work:
$env.gettype()
dir $env
dir $env:
but this type query on $host does so I'm assuming $host is a .net variable but $env is not?
$host.gettype()
I found that env: also works with dir (aka get-childitem) but this colon is yet another type of notation i'm unfamiliar with and things are starting to get very confusing now. This does not seem to be a string format in this case which I have seen some google posts about so what is it? It behaves like a member selection or dictionary key specifier. If it is a member selector or dictionary key then i would expect get-member to work because it is a standard object.
This outputs the variables and values that I wanted but I don't understand why this syntax is used. This is not DOS syntax either so what's going on here?
dir env:
But dir $env seems to equate to dir $env:userprofile???? why?
Therefore $host appears to be a .net object but $env or env: is something else completely different and I've no idea what object type it is in the grand scheme of things and cannot seem to query it's type with by conventional means. Initial thoughts are that it is a list object of sorts because get-childitem works with it but other than that I'm completely lost.
I'm clearly missing something here so can someone steer me in the right direction please?
Get-Help 'about_Providers' -ShowWindow shows that env: is drive in Environment Provider, i.e. one of Windows PowerShell providers.
BUILT-IN PROVIDERS: Windows PowerShell includes a set of built-in
providers that you can use to access the different types of data
stores.
Provider Drive Data store
-------- ----- ----------
Alias Alias: Windows PowerShell aliases
Certificate Cert: x509 certificates for digital signatures
Environment Env: Windows environment variables
FileSystem * File system drives, directories, and files
Function Function: Windows PowerShell functions
Registry HKLM:, HKCU: Windows registry
Variable Variable: Windows PowerShell variables
WSMan WSMan: WS-Management configuration information
* The FileSystem drives vary on each system.
You can also create your own Windows PowerShell providers, and you can
install providers that others develop. To list the providers that are
available in your session, type: get-psprovider.
That's why Get-ChildItem env: works in contrary to dir $env:, dir $env etc.
First thing to note is that $env and $env:username are not related. $env is just a variable and normally it does not exists, because nobody assign anything to it. Using colon in variable name (like $env:username, with exception to some predefined prefixes: global:, script:, local:, private: and variable:) is a special syntax, which allows to access to PowerShell provider item content with variable syntax. It works with any PowerShell provider which implement content cmdlets: ${C:\Windows\System.ini} or $function:prompt. That syntax is equivalent of calling of Get-Content or Set-Content for given PowerShell path.
My 2 cents:
Try get-psdrive and you will get something like:
Name Used (GB) Free (GB) Provider Root
Env Environment
So it seems to be something like a driver in batch.

How to dynamically create an environment variable?

I am using powershell script to set some environment variable--
$env:FACTER_Variable_Name = $Variable_Value
FACTER is for using these in the puppet scripts.
My problem is - the variable name and variable value both are dynamic and getting read from a text file.
I am trying to use
$env:FACTER_$Variable_Name = $Variable_Value
But $ is not acceptable syntax. When I enclose it in double quotes, the variable value is not getting passed. Any suggestion how to use it dynamically.
Thanks in Advance
[Environment]::SetEnvironmentVariable("TestVariable", "Test value.", "User")
This syntax allows expressions in the place of "TestVariable", and should be enough to create a profile-local environment variable. The third parameter can be "Process", this makes new vars visible in Get-ChildItem env: or "Machine" - this required administrative rights to set the variable. To retrieve a variable set like this, use [Environment]::GetEnvironmentVariable("TestVariable", "User") (or matching scope if you choose another).
On Powershell 5, to set dynamically an environment variable in the current shell I use Set-Item:
>$VarName="hello"
>Set-Item "env:$VarName" world
>$env:hello
world
>
and of course to persist the variable I use C# [Environment]::SetEnvironmentVariable("$VarName", "world", "User")
In pure PowerShell, something like this:
$Variable_Name = "foo"
$FullVariable_Name = "FACTER_$Variable_Name"
$Variable_Value = "Hello World"
New-Item -Name $FullVariable_Name -value $Variable_Value -ItemType Variable -Path Env:
I'm using the New-Item cmdlet to add a new variable, just have to specify the -itemtype and -path

How to perform Import-Module in powershell script used in File Server Resource Manager Classification script

I am trying to use the active directory PowerShell module inside a classification rule in File server resource manager on windows server 2012 R2.
When I try to just perform:
Import-Module ActiveDirectory
It will crash (I assume) and not update the classification property anymore.
I tried setting the script parameter -ExecutionPolicy Unrestricted, but that didn't help.
Anyone know how to get it to work?
non working code:
# Global variables available:
# $ModuleDefinition (IFsrmPipelineModuleDefinition)
# $Rule (IFsrmClassificationRule)
# $PropertyDefinition (IFsrmPropertyDefinition)
#
# And (optionally) any parameters you provide in the Script parameters box below,
# i.e. "$a = 1; $b = 2" . The string you enter is treated as a script and executed so the
# variables you define become globally available
# optional function to specify when the behavior of this script was last modified
# if it consumes additional files. emit one value of type DateTime
#
# function LastModified
# {
# }
# required function that outputs a value to be assigned to the specified property for each file classified
# emitting no value is allowed, which causes no value to be assigned for the property
# emitting more than one value will result in errors during classification
# begin and end are optional; process is required
#
function GetPropertyValueToApply
{
# this parameter is of type IFsrmPropertyBag
# it also has an additional method, GetStream, which returns a IO.Stream object to use for
# reading the contents of the file. Make sure to close the stream after you are done reading
# from the file
param
(
[Parameter(Position = 0)] $PropertyBag
)
process
{
Import-Module activedirectory
$users = Get-ADUser -filter * -Properties SID,Department
return "dummy result";
}
}
As note: This works perfectly fine in a PowerShell console; that isn't the issue. It's running the code as classifier for the file server resource manager.
Worked my way around it now by just creating a CSV file with the result of the Get-ADUser and loading that inside the script for now (so I don't require any non standard modules). But it would be nicer to just run this without a dependency on some external task.
The classification script is executed from a the File Server Resource Manager Service (not from the UI you are looking at), which is running under the system account.
So you either need to modify under which account the service is running or give the account rights to access the objects you require to access. In my case Active Directory.