PowerShell - Merge two variables into one - powershell

I'm learning PowerShell so please forgive (what I'm sure is) a simple question.
I'm used to coding BATCH scripts and if I wanted to merge %USERDOMAIN% and %USERNAME% I would:
set zFullUsername=%USERDOMAIN%\%USERNAME%
echo %zFullUsername%
How can I do the same in PowerShell?
Thank you for your time.

On a supported Operating System, I wouldn't even bother with environment variables for this:
$zFullUsername = whoami
Then just access it as required:
$zFullUsername

In PowerShell, you can access environment variables in a few different ways. The way I recommend is to use the $env:VAR variable to access them.
$user = $env:USERNAME
$domain = $env:USERDOMAIN
echo "$domain\$user"
Note: \ is not an escape character in the PowerShell parser, ` is.
Similarly to rendering the echo command (echo is an alias of Write-Output btw) you can create a username variable like so:
$fullUserName = "$domain\$user"
Or you can skip right to creating $fullUserName straight from the environment variables:
$fullUserName = "${env:USERDOMAIN}\${env:USERNAME}"
Note: When variables have non-alphanumeric characters in them, the ${} sequence tells PowerShell everything between the ${} is part of the variable name to expand.
It seems the : in $env:VAR is actually an exception to this rule, as"Username: $env:USERNAME" does render correctly. So the ${} sequence above is optional.
To avoid confusion when trying to apply this answer in other areas, if you needed to insert the value of an object property or some other expression within a string itself, you would use a sub-expression within the string instead, which is the $() sequence:
$someVar = "Name: $($someObject.Name)"
When using either ${} or $(), whitespace is not allowed to pad the outer {} or ().

Related

Pipeline Array Variable (Classic) Causing powershell exception [duplicate]

I try to concatenate string to construct a path:
$SourceDirectoryPath = $(System.DefaultWorkingDirectory) + "/solution/project/bin/Debug"
$TargetFilePath = $(System.DefaultWorkingDirectory) + "/solution/project/bin/Debug/" + $(Release.ReleaseName) +$(Release.EnvironmentName)
but instead of getting string concatenated I get error for the second line:
d:\a\r1\a : The term 'd:\a\r1\a' is not recognized as the name of a
cmdlet, function, script file, or operable program. Check the
spelling of the name, or if a path was included, verify that the path
is correct and try again. At
D:\a_temp\9de874c9-3acd-4a19-a4dd-763074d38e40.ps1:2 char:25
where obviously d:\a\r1\a is a $(System.DefaultWorkingDirectory) but why it throws this error instead of just concatenating the string?
tl;dr
It is Azure that expands $(System.DefaultWorkingDirectory) before PowerShell sees the resulting commands; if the expanded $(...) value is to be seen as a string by PowerShell, it must be enclosed in quotes ('$(...)'):
Using $(...) (Azure macro syntax) embeds the Azure variable's verbatim value in the command text that PowerShell ends up interpreting.
Note: Azure's macro syntax - which is evaluated before PowerShell sees the resulting command text - is not to be confused with PowerShell's own subexpression operator, $(...).
For string values this means that you situationally have to surround the macro with quotes in order to make it work syntactically in PowerShell code, for which '...'-quoting (single-quoting) is best: '$(System.DefaultWorkingDirectory)'
Shayki Abramczyk's answer provides an effective solution, but let me provide some background information:
The variable expansion (substitution) that Azure performs via macro syntax ($(...)) functions like a preprocessor: it replaces the referenced variable with its verbatim value.
You need to make sure that this verbatim value works syntactically in the context of the target command.
As currently written:
$SourceDirectoryPath = $(System.DefaultWorkingDirectory) + "/solution/project/bin/Debug"
turns into the following command seen by PowerShell, assuming that the value of Azure property System.DefaultWorkingDirectory is d:\a\r1\a:
$SourceDirectoryPath = d:\a\r1\a + "/solution/project/bin/Debug"
This is a broken PowerShell command, because d:\a\r1\a - due to lack of quoting - is interpreted as a command name or path; that is, an attempt is made to execute putative executable d:\a\r1\a - see about_Parsing.
Therefore, in order for PowerShell to recognize the Azure-expanded value d:\a\r1\a as a string, you need to quote it - see about_Quoting_Rules.
Since the expanded-by-Azure value needs no further interpolation, single quotes are the best choice (for both operands, actually):
$SourceDirectoryPath = '$(System.DefaultWorkingDirectory)' + '/solution/project/bin/Debug'
In fact, you don't need string concatenation (+) at all in your case:
$SourceDirectoryPath = '$(System.DefaultWorkingDirectory)/solution/project/bin/Debug'
You could even combine that with expandable PowerShell strings ("..."), as long as the Azure-expanded value doesn't contain $-prefixed tokens that PowerShell could end up interpreting (unless that is your (unusual) intent).
One caveat re something like "$(System.DefaultWorkingDirectory)/$projectRoot/bin/Debug" (mixing an Azure-expanded value with a PowerShell variable reference) is that Azure's macro syntax ($(...)) looks the same as PowerShell's own subexpression operator, which is typically - but not exclusively - used in order to embed expressions in expandable strings (e.g., in pure PowerShell code, "1 + 1 equals $(1 + 1)").
As of this writing, the Define variables Azure help topic doesn't spell it out, but based on the official comment in a GitHub docs issue, ambiguity is avoided as follows:
There is no escape mechanism; instead, $(...) constructs that do not refer to Azure variables are left unchanged and therefore passed through to PowerShell.
In the typical case, PowerShell expressions will not look like an Azure variable reference (e.g, $($foo.bar) rather than $(foo.bar)), though hypothetically there can be ambiguity: $(hostname), which is a valid PowerShell subexpression, could be preempted by Azure if a hostname Azure variable were defined.
In such a corner case, the solution is to avoid use of an inline script and instead place the code in an external script file.
You need to add quotes " " in the variables:
$SourceDirectoryPath = "$(System.DefaultWorkingDirectory)" + "/solution/project/bin/Debug"
$TargetFilePath = "$(System.DefaultWorkingDirectory)" + "/solution/project/bin/Debug/" + "$(Release.ReleaseName)" + "$(Release.EnvironmentName)"
This should work as well. $( ) outside of doublequotes would only be used to combine two or more statements. Most people don't even know about it.
This is actually incorrect. I didn't know Azure Pipeline syntax. It just shows how confusing combining both Powershell and Azure Pipeline can be. This would work if $System were a Powershell object, not an Azure macro.
$SourceDirectoryPath = $System.DefaultWorkingDirectory + '/solution/project/bin/Debug'

Is it possible to dot source a string variable in PowerShell?

I know I can dot source a file:
. .\MyFunctions.ps1
But, I would like to dot source the commands in a string variable:
. $myFuctions
I see that this is possible:
.{$x=2}
And $x equals 2 after the script block is sourced.
But... .{$myFunctions} does not work.
I tried $myFunctions | Invoke-Expression, but it doesn't keep the source function in the current scope. The closest I have been able to come up with is to write the variable to a temporary file, dot source the file, and then remove the file.
Inevitably, someone will ask: "What are you trying to do?" So here is my use case:
I want to obfuscate some functions I intend to call from another script. I don't want to obfuscate the master script, just my additional functions. I have a user base that will need to adjust the master script to their network, directory structure and other local factors, but I don't want certain functions modified. I would also like to protect the source code. So, an alternate question would be: What are some good ways to protect PowerShell script code?
I started with the idea that PowerShell will execute a Base64-encoded string, but only when passed on the command line with -EncodedCommand.
I first wanted to dot source an encoded command, but I couldn't figure that out. I then decided that it would be "obfuscated" enough for my purposes if I converted by Base64 file into a decode string and dot sourced the value of the string variable. However, without writing the decoded source to a file, I cannot figure out how to dot source it.
It would satisfy my needs if I could Import-Module -EncodedCommand .\MyEncodedFile.dat
Actually, there is a way to achieve that and you were almost there.
First, as you already stated, the source or dot operator works either by providing a path (as string) or a script block. See also: . (source or dot operator).
So, when trying to dot-source a string variable, PowerShell thinks it is a path. But, thanks to the possibility of dot-sourcing script blocks, you could do the following:
# Make sure everything is properly escaped.
$MyFunctions = "function Test-DotSourcing { Write-Host `"Worked`" }"
. { Invoke-Expression $MyFunctions }
Test-DotSourcing
And you successfully dot-sourced your functions from a string variable!
Explanation:
With Invoke-Expression the string is evaluated and run in the child scope (script block).
Then with . the evaluated expressions are added to the current scope.
See also:
Invoke-Expression
About scopes
While #dwettstein's answer is a viable approach using Invoke-Expression to handle the fact that the function is stored as a string, there are other approaches that seem to achieve the same result below.
One thing I'm not crystal clear on is the scoping itself, Invoke-Expression doesn't create a new scope so there isn't exactly a need to dot source at that point...
#Define your function as a string
PS> $MyUselessFunction = "function Test-WriteSomething { 'It works!' }"
#Invoke-Expression would let you use the function
PS> Invoke-Expression $MyUselessFunction
PS> Test-WriteSomething
It works!
#Dot sourcing works fine if you use a script block
PS> $ScriptBlock = [ScriptBlock]::Create($MyUselessFunction)
PS> . $ScriptBlock
PS> Test-WriteSomething
It works!
#Or just create the function as a script block initially
PS> $MyUselessFunction = {function Test-WriteSomething { 'It works!' }}
PS> . $MyUselessFunction
PS> Test-WriteSomething
It works!
In other words, there are probably a myriad of ways to get something similar to what you want - some of them documented, and some of them divined from the existing documentation. If your functions are defined as strings, then Invoke-Expression might be needed, or you can convert them into script blocks and dot source them.
At this time it is not possible to dot source a string variable.
I stand corrected! . { Invoke-Expression $MyFunctions } definitely works!

Using variable which contains $ as password

I'm trying to deploy something through Octopus V4 and in my project variables, I got a password in which I have the special character $.
Assigning this Octo variable to a PowerShell variable will transform the string because PowerShell surely thinks that there is a variable in it. For instance, if I've got this:
azerty$qwerty
I'll end up with this:
azertyerty
So when it comes to start the deployed service with the appropriate credentials, it won't work. Just for the record: before starting the service, I'm creating it with the New-Service command in the deployment script of the project.
I read here and there that I need to use single quotes instead of double quotes to make it work but here my hands are tied since I'm just getting the variable from Octopus doing that:
$mypassword = $OctopusParameters["password"]
Any clue about how to keep the full string, $ included?
Please see About Quoting Rules as this may help.
When you enclose a string in single-quotation marks (a single-quoted string), the string is passed to the command exactly as you type it. No substitution is performed. When you enclose a string in double quotation marks (a double-quoted string), variable names that are preceded by a dollar sign ($) are replaced with the variable's value before the string is passed to the command for processing.
See below for an example of this behavior:
PS> $for = "my string"
PS> "test$for"
testmy string
PS> 'test$for'
test$for
Octopus may have its own way of dealing with this. I would try using double single quotes around the string and see if that helps, like ''my$string''.
I did also read in a forum that someone had success using the following cmdlet for password:
Set-OctopusVariable -Name 'password' -Value 'password123$'
This might be a silly question, but have you tried this already and it didn't work?
If you are using Octopus with the syntax:
$mypassword = $OctopusParameters["password"]
Octopus will not do any replacement of the value set in the Octopus parameter. Your foreseen issue will only appear if you are setting the password in double quotes inside Powershell directly, as in:
$mypassword = "azerty$qwerty"
And if you are doing this in Powershell, the correct way to set it will be to escape the $ with the tick (`) as in:
$mypassword = "azerty`$qwerty"

Powershell - add variables inside a json string

I have the following json code in my powershell script.
I set the $variable to 1111111111
$jsonfile = '{"Version": "2012-10-17","Statement": {"Effect": "Allow","Action": "sts:AssumeRole","Resource": "arn:aws:iam::$variable:role/xxxxxx"}}'
The output gives ....arn:aws:iam::$variable:role/xxxxxx..... instead of ....arn:aws:iam::1111111111:role/xxxxxx
The problem is that I must use the single quote for the json string otherwise I will get an error. If I use single quote I wont be able to put the variables inside the string. How do I workaround this problem?
There are various ways to solve your problem, but perhaps the easiest approach is to use PowerShell's string interpolation:
use a double-quoted string overall to enable interpolation of embedded variable references and subexpressions ($(...)).
escape embedded " chars. as `" (using backticks)
disambiguate variable references by enclosing the variable name in {...}.
Simplified example:
PS> $variable='111'
PS> "{`"Version`": `"arn:aws:iam::${variable}:role/xxxxxx`"}}"
{"Version": "arn:aws:iam::111:role/xxxxxx"}}
Note that enclosing variable names in {...} in interpolated strings is only necessary if the following char. could be misinterpreted as part of the variable name.
A : following the variable name - as is the case here - is such a case, because PS variables can have a scope specifier preceding the variable name that is separated from the variable name with :, such as in $env:USERNAME.
DAXaholic's helpful answer shows an alternative based on PowerShell's binary -f operator, which is essentially the same as the .NET framework's String.Format method; as such:
it introduces additional complexity, such as needing to know what its escaping rules are ({ chars. must be escape as {{, and how to format its arguments specified on the RHS of -r ({0} refers to the 1st RHS argument, ...)
on the flip side, -f offers many sophisticated formatting options.
Also, consider use of the Convert*-Json cmdlets his answer demonstrates: even though they're less performant, they ultimately make manipulation of JSON much easier and more robust.
Alternatives in the realm of native PowerShell code:
String concatenation with the binary + operator:
'{"Version": "arn:aws:iam::' + $variable + ':role/xxxxxx"}}'
String templating with $ExecutionContext.InvokeCommand.ExpandString():
$variable='111'
$tmpl = '{"Version": "arn:aws:iam::${variable}:role/xxxxxx"}}' # string template *literal*
$ExecutionContext.InvokeCommand.ExpandString($tmpl) # performs on-demand interpolation
Another solution would be
$jsonfile = '{{"Version": "2012-10-17","Statement": {{"Effect": "Allow","Action": "sts:AssumeRole","Resource": "arn:aws:iam::{0}:role/xxxxxx"}}}}' -f $variable
So you have to escape the braces with another brace but in your case you have fewer braces than quotes so it is "less obfuscation" :)
In your case, maybe the simplest solution is just concatenating the strings together instead of using string formatting / interpolation.
In addition you could also go the way with the JSON cmdlets:
$jsonfile |
ConvertFrom-Json |
% { $_.Statement.Resource = "arn:aws:iam::${variable}:role/xxxxxx"; $_ } |
ConvertTo-Json

What does the period '.' operator do in powershell?

This is a weird one. Normally when I execute an external command from powershell I use the & operator like this:
& somecommand.exe -p somearguments
However, today I came across the . operator used like this:
.$env:systemdrive\chocolatey\chocolateyinstall\chocolatey.cmd install notepadplusplus
What purpose does the period serve in this scenario? I don't get it.
The "." dot sourcing operator will send AND receive variables from other scripts you have called. The "&" call operator will ONLY send variables.
For instance, considering the following:
Script 1 (call-operator.ps1):
clear
$funny = "laughing"
$scriptpath = split-path -parent $MyInvocation.MyCommand.Definition
$filename = "laughing.ps1"
"Example 1:" # Call another script. Variables are passed only forward.
& $scriptpath\$filename
"Example 2:" # Call another script. Variables are passed backwards and forwards.
. $scriptpath\$filename
$variableDefinedInOtherScript
Script 2 (laughing.ps1):
# This is to test the passing of variables from call-operator.ps1
"I am $funny so hard. Passing variables is so hilarious."
$variableDefinedInOtherScript = "Hello World!"
Create both scripts and ONLY run the first one. You'll see that the "." dot sourcing operator sends and receives variables.
Both have their uses, so be creative. For instance, the "&" call operator would be useful if you wanted to modify the value(s) of variables in another script while preserving the original value(s) in the your current script. Kinda a safeguard. ;)
The Short:
It is a Special Operator used to achieve what regular operators cannot achieve. This particular operator . actually has two distinctively different Special Operator use cases.
The Long:
As with any other language, scripting or otherwise, PowerShell script also supports many different types of Operators to help manipulate values. These regular operators include:
Arithmetic
Assignment
Comparison
Logical
Redirection
List item
Split and Join
Type
Unary
However, PowerShell also supports whats known as Special Operators which are used to perform tasks that cannot be performed by the other types of operators.
These Special Operators Include:
#() Array subexpression operator
& Call operator
[ ] Cast operator
, Comma operator
. Dot sourcing operator
-f Format operator
[ ] Index operator
| Pipeline operator
. Property dereference operator
.. Range operator
:: Static member operator
$( ) Subexpression operator
. Dot sourcing operator: is used in this context to allow a script to run in the current scope essentially allowing any functions, aliases, and variables which has been created by the script to be added to the current script.
Example:
. c:\scripts.sample.ps1
NoteThat this application of the . Special Operator is followed by a space to distinguish it from the (.) symbol that represents the current directory
Example:
. .\sample.ps1
. Property dereference operator: Allows access to the properties and methods of of an object which follows the . by indicating that the expression on the left side of the . character is an object and the expression on the right side of the is an object member (a property or method).
Example:
$myProcess.peakWorkingSet
(get-process PowerShell).kill()
Disclaimer & Sources:
I had the same question while looking at a PowerShell script that I was trying to expand on its feature sets and landed here when doing my research for the answer. However I managed to find my answer using this magnificent write up on the Microsoft Development Network supplemented with this further expansion of the same ideas from IT Pro.
Cheers.
The dot is a call operator:
$a = "Get-ChildItem"
. $a # (executes Get-ChildItem in the current scope)
In your case, however, I don't see what it does.
.Period or .full stop for an objects properties; like
$CompSys.TotalPhysicalMemory
See here: http://www.computerperformance.co.uk/powershell/powershell_syntax.htm#Operators_
This answer is to expand slightly upon those already provided by David Brabant and his commenters. While those remarks are all true and pertinent, there is something that has been missed.
The OPs use of & when invoking external commands is unnecessary. Omitting the & would have no effect (on the example of his usage). The purpose of & is to allow the invocation of commands whose names are the values of a (string) expression. By using the & above, powershell then (essentially) treats the subsequent arguments as strings, the first of which is the command name that & duly invokes. If the & were omitted, powershell would take the first item on the line as the command to execute.
However, the . in the second example is necessary (although, as noted by others, & would work just as well in this case). Without it, the command line would begin with a variable access ($env:systemdrive) and so powershell would be expecting an expression of some form. However, immediately following the variable reference is a bare file path which is not a valid expression and will generate an error. By using the . (or &) at the beginning of the line, it is now treated as a command (because the beginning doesn't look like a valid expression) and the arguments are processed as expandable strings (" "). Thus, the command line is treated as
. "$env:systemdrive\chocolatey\chocolateyinstall\chocolatey.cmd" "install" "notepadplusplus"
The first argument has $env:systemdrive substituted into it and then . invokes the program thus named.
Note: the full description of how powershell processes command line arguments is way more complicated than that given here. This version was cut down to just the essential bits needed to answer the question. Take a look at about_Parsing for a comprehensive description. It is not complete but should cover most normal usage. There are other posts on stackoverflow and github (where powershell now resides) that cover some of the seemingly quirky behaviour not listed in the official documentation. Another useful resource is about_Operators though again this isn't quite complete. An example being the equivalence of . and & when invoking something other than a powershell script/cmdlet getting no mention at all.