Powershell Script wont add $PSDefaultParameterValues to $profile - powershell

I'm writing a quick Powershell script to import modules and update some default parameters on various machines. I'm running into an issue where in my script when I add $PSDefaultParameterValues to the $profile it changes to System.Management.Automation.DefaultParameterDictionary which then throws an error of not being recognized as the name of a cmdlet.
Here is the code in my ps1 script
Add-Content -Path $PROFILE -Value "$PSDefaultParameterValues = #{}"
Here is what gets added to the profile
System.Management.Automation.DefaultParameterDictionary = #{}
I've tried everything from using Set-Content to using variables to avoid quotation confusion.
I appreciate the help!

Use single quotes
Add-Content -Path $PROFILE -Value '$PSDefaultParameterValues = #{}'
These are literal strings so any variables will not expand.

To add to tonypags' helpful answer:
Double-quoted PowerShell strings ("...") are expandable strings, i.e they perform string interpolation of embedded variable references (e.g. $PSDefaultParameterValues) and subexpressions (e.g. $(1 + 2))
Single-quoted strings ('...') are verbatim strings, i.e. their content is used as-is (verbatim, literally).
Thus, the automatic $PSDefaultParameterValues variable was expanded in your "..." string, which in essence means it was replaced with its .ToString() representation - which in this case is simply the type name of the variable's value.
If your entire string is meant to be used verbatim, use '...' quoting, as shown in tonypags' answer.
If you do need expansions (string interpolation), but need to selectively suppress them, escape $ characters as `$, using ` (a backtick), PowerShell's escape character, as shown in the following example:
$enc = 'utf8'
# Note the backtick (`) before $PSDefaultParameterValues.
# NOTE: It is only the *outer* quoting that determines
# whether expansion is performed.
Add-Content -Path $PROFILE `
-Value "`$PSDefaultParameterValues = #{ '*:Encoding' = '$enc' }"

Related

New-MailboxExportRequest quote issue

I'm trying to get the following command into a program I am developing in PowerShell studio, I'm having some issues figuring out the escaping sequence here, and testing takes forever since I have to import the PST just to view the data :(
Consider the following command which works correctly from the shell...
New-MailboxExportRequest -Mailbox user#domain.com -Name JobName -IncludeFolders "#Inbox#/*","#SentItems#" -ContentFilter {(Received -gt "06/10/2022") -and (Sent -gt "06/10/2022")} -FilePath "\\Server\Folder\MyPST.pst"
I am using variables inside program designer, so my actual code looks like this...
New-MailboxExportRequest -Mailbox $mailbox -Name $jobname -IncludeFolders $IncludeFolders -ContentFilter {(Received -gt "$RecDate") -and (Sent -gt "$SentDate")} -FilePath "$FilePath"
Basically I need to get the first code sample working using variables, however the -IncludeFolders and -ContentFilter parameters require some escaping I can't seem to figure out. Any and all help is much appreciated.
tl;dr:
You need to enclose your -ContentFilter argument in "..." overall in order to support embedding variable values, which you can themselves enclose in embedded '...' quoting:
-ContentFilter "(Received -gt '$RecDate') -and (Sent -gt '$SentDate')"
No special syntax considerations apply with respect to using a variable with -IncludeFolders: Just make sure that variable $IncludeFolders contains an array of strings identifying the target folders; applied to your example:
$IncludeFolders = '#Inbox#/*', '#SentItems#'
If the list of folders must be parsed from user input provided as a single string ($textbox5.text in this example) do the following to convert this single string to an array of names:
# If $textbox5.text contains string '#Inbox#/*, #SentItems#',
# the result is the same as above.
$IncludeFolders = ($textbox5.text -split ',').Trim()
Read on for background information and caveats re -ContentFilter.
Note:
The -ContentFilter information below also applies to other Exchange cmdlets that accept filters, such as Get-Recipient with its -Filter parameter.
It also applies analogously to the -Filter parameter used with AD (Active Directory) cmdlets such as Get-ADUser, although the situation there is complicated by the fact that the AD provider does support evaluation of PowerShell variables, but only in simple cases - see this answer.
New-MailboxExportRequest's -ContentFilter expects a string argument, not a script block ({ ... }), which you've used in your question.
While you can situationally get away with a script block, it is best avoided for two reasons:
When a script block is used as a string (converted to one), string interpolation (embedding the values of variables in the string) is not supported - a script block stringifies to its verbatim content (sans { and }).
Conceptually, use of a script block can give the mistaken impression that an arbitrary piece of PowerShell code may be passed, which is not the case: -ContentFilter supports a domain-specific syntax only that only emulates a limited set of PowerShell features, called OPath filter syntax.
Notably, evaluation of PowerShell variables is not supported - their values must be embedded in the string up front, by PowerShell, using string interpolation.
Therefore:
It's best to use a string argument with -ContentFilter to begin with.
Since you do need to embed variable values, you need string interpolation, via an expandable (double-quoted) string ("..."), inside of which you may quote values with '...' for syntactical convenience.
In case there is no need to embed variable values, use a verbatim (single-quoted) string ('...'), inside of which you may quote values with "...", as in your question. (In effect, this is the equivalent of the (ill-advised) use of { ... }).
Applied to your example: The following embeds the (stringified) values of variables $RecDate and $SentDate in your -ContentFilter argument:
# Note the use of "..." for the outer quoting, and
# '...' for the embedded quoting.
-ContentFilter "(Received -gt '$RecDate') -and (Sent -gt '$SentDate')"
Caveats:
If the values of the variables to embed themselves contain ', use escaped embedded "..." quoting instead; e.g., `"$var`"
If a value could contain ' and/or " and you don't know which, you'd have to use an extra layer of up-front string interpolation on the PowerShell side to escape one of them, depending on the embedded quoting character chosen, using $(...), the subexpression operator, using a -replace operation:
In this escaping you must technically satisfy the OPath filter syntax requirements; the linked help OPath topic suggests that, like in PowerShell, '' can be used to escape ' inside a '...' string; e.g.: -ContentFilter "Body -like '$($var -replace "'", "''")*'"
While, as stated, OPath filters do not support evaluating PowerShell variable references in general, the following automatic variables - which are conceptually constants - are recognized (the linked help topic calls them system values):
$true, $false
$null
Therefore, if you use "..." for the outer quoting (for string interpolation), the $ in these variables must be escaped with ` (the so-called backtick, PowerShell's escape character), to prevent PowerShell from replacing these variable references with their values up front:
`$true, `$false
`$null

add-member weirdness with type assignment

In the code below, why does the [int] type assignment for name1 get ignored and is actually treated as part of the value? Bug?
$name1 = 4
$name2 = 4
$name3 = 4
$myObject = New-Object System.Object
$myObject | Add-Member -type NoteProperty -name name1 -Value [int]$name1
$myObject | Add-Member -type NoteProperty -name name2 -Value $($name2 -as [int])
$myObject | Add-Member -type NoteProperty -name name3 -Value $([int]$name3)
$myObject
Output:
name1 name2 name3
----- ----- -----
[int]4 4 4
Powershell version:
get-host | select-object version
Version
-------
5.1.19041.1023
There's good information in the existing answers, but let me attempt a systematic overview:
tl;dr
In order to pass the output from an expression (e.g. [int] $name1), or (nested) command (e.g. Get-Date -Format yyyy) as an argument to a command (e.g. Add-Member), enclose it in (...), the grouping operator:
Add-Member -type NoteProperty -name name1 -Value ([int] $name1)
By contrast, $(...), the subexpression operator is typically not needed in this scenario, and its use can have side effects - see this answer.
In short: (Outside of expandable strings, "..."), you only ever need $(...) to either enclose a language statement (such as an if statement or foreach loop) or multiple statements (any mix of commands, expressions, and language statements separated with ;).
PowerShell's parsing modes:
PowerShell has two fundamental parsing modes:
argument mode, which works like shells.
In argument mode, the first token is interpreted as a command name (the name of cmdlet, function, alias, or the name of path of an external executable or .ps1 script), followed by a whitespace-separated list of arguments, where strings may be unquoted[1] and arguments composed of a mix of literal parts and variable references are typically treated like expandable strings (as if they were enclosed in "...").
expression mode, which works like programming languages, where strings must be quoted, and operators and language statements such as assignments, foreach and while loops, casts can be used.
The conceptual about_Parsing provides an introduction to these modes; in short, it is the first token in a given context that determines which mode is applied.
A given statement may be composed of parts that are parsed in either mode, which is indeed what happens above:
Because your statement starts with a command name (Add-Member), it is parsed in argument mode.
(...) forces a new parsing context, which in the case at hand ([int] $name1) is parsed in expression mode, due to starting with [).
What is considered a metacharacter (a character with special, syntactic meaning) differs between the parsing modes:
[ and = are special only in expression mode, not in argument mode, where they are used verbatim.
Conversely, a token-initial # followed by a variable name is only special in argument mode, where it is used for parameter splatting.
Compound argument [int]$name1 is therefore treated as if it were an expandable string, and results in verbatim string [int]4.
Some expressions do not require enclosing in (...) when used as command arguments (assume $var = 'Foo'):
A stand-alone variable reference (e.g. Write-Output $var or Write-Output $env:OS)
Property access on such a reference (e.g. Write-Output $var.Length)
Method calls on such a reference (e.g. Write-Output $var.ToUpper())
Note that these arguments are passed with their original data type, not stringified (although stringification may be performed by the receiving command).
Pitfalls:
You sometimes need to use "..." explicitly in order to suppress property-access interpretation and have a . following a variable reference be interpreted verbatim (e.g. Write-Output "$var.txt" in order to get verbatim foo.txt).
If you use $(...) as part of a compound argument without explicit "..." quoting, that argument is broken into multiple arguments if the $(...) subexpression starts the argument (e.g., Write-Output $('a' + 'b')/c passes two arguments, verbatim ab and /c, whereas Write-Output c/$('a' + 'b') passed just one, verbatim c/ab).
Similarly, mixing quoted and unquoted strings to form a single argument only works if the argument starts with an unquoted token (e.g., Write-Output One"$var"'$Two' works as expected and yields verbatim OneFoo$Two, but Write-Output 'One'"$var"'$Two' is passed as three arguments, verbatim One, Foo, and $Two).
In short:
The exact rules for how arguments are parsed are complex:
This answer summarizes the rules for unquoted arguments.
This answer summarizes mixing quoted and unquoted strings in a single argument
This answer) (bottom section) summarizes PowerShell's string literals in general.
To be safe, avoid use of $(...) outside "..." and avoid mixing quoting styles in a single string argument; either use a (single) "..." string (e.g. Write-Output "$(Split-Path $PROFILE)/foo.txt" or ) or string concatenation in an expression (Write-Output ('One' + $var + '$Two')
[1] Assuming they contain neither spaces nor any of PowerShell's metacharacters (see this answer). While quoting typically takes the form of enclosing the entire argument in single or double-quotes, as appropriate (e.g. 'foo bar', "foo $var"), it is also possible to quote (escape) individual characters (e.g. foo` bar), using the backtick (`), PowerShell's escape character.
From the about_Parsing help file:
Argument mode
When parsing, PowerShell first looks to interpret input as an expression. But when a command invocation is encountered,
parsing continues in argument mode.
Argument mode is designed for parsing arguments and parameters for
commands in a shell environment. All input is treated as an expandable
string unless it uses one of the following syntaxes:
...
You can enclose the code in a subexpression ($(...)) to avoid PowerShell treating [int]$name1 as an expandable string, as you've already found :-)
This is by design. You need to wrap in parentheses in order to evaluate the expression before passing it into the function.
Its the same if you try to put a function call there for instance:
$myObject | Add-Member -type NoteProperty -name name1 -Value Write-Host
returns as:
name1
-----
Write-Host
And Write-Host [string]2 returns [string]2 not 2

Powershell indexing for creating user logon name

I'm trying to make a user creation script for my company to make things more automated.
I want the script to take the Firstname + Lastname[0] to make the users logon name, but i can't get the syntax right,
I have tried writing {} and () but no luck there.
that's the original peace from my script
New-ADuser...........-UserPrincipalName $fname+$lname[0]
any tips?
Gabriel Luci's helpful answer provides an effective solution and helpful pointers, but it's worth digging deeper:
Your problem is that you're trying to pass expression $fname+$lname[0] as an argument, which requires enclosing (...):
New-ADuser ... -UserPrincipalName ($fname+$lname[0])
PowerShell has two distinct parsing modes, and when a command (such as New-ADUser) is called, PowerShell operates in argument mode, as opposed to expression mode.
Enclosing an argument in (...) forces a new parsing context, which in the case of $fname+$lname[0] causes it to be parsed in expression mode, which performs the desired string concatenation.
In argument mode, unquoted arguments are implicitly treated as if they were enclosed in "...", i.e., as expandable strings under the following circumstances:
If they don't start with (, #, $( or #(.
If they either do not start with a variable reference (e.g., $var) or do start with one, but are followed by other characters that are considered part of the same argument (e.g., $var+$otherVar).
Therefore, $fname+$lname[0] is evaluated as if "$fname+$lname[0]" had been passed:
The + become part of the resulting string.
Additionally, given that inside "..." you can only use variable references by themselves (e.g., $fname), not expressions (e.g., $lname[0]), $lname[0] won't work as intended either, because the [0] part is simply treated as a literal.
Embedding an expression (or a command or even multiple expressions or commands) in "..." requires enclosing it in $(...), the subexpression operator, as in Gabriel's answer.
For an overview of PowerShell's string expansion rules, see this answer.
The following examples use the Write-Output cmdlet to illustrate the different behaviors:
$fname = 'Jane'
$lname = 'Doe', 'Smith'
# WRONG: Same as: "$fname+$lname[0]", which
# * retains the "+"
# * expands array $lname to a space-separated list
# * treats "[0]" as a literal
PS> Write-Output -InputObject $fname+$lname[0]
Jane+Doe Smith[0]
# OK: Use an expression via (...)
PS> Write-Output -InputObject ($fname+$lname[0])
JaneDoe
# OK: Use an (implicit or explicit) expandable string.
PS> Write-Output -InputObject $fname$($lname[0]) # or: "$fname$($lname[0])"
JaneDoe
# OK: Use an intermediate variable:
PS> $userName = $fname + $lname[0]; Write-Output -InputObject $userName
Use a string for the UserPrincipalName, with the variables in the string:
New-ADuser -UserPrincipalName "$fname$($lname[0])"
PowerShell can usually figure out when you put a variable inside a string. When it can't, like in the case of $lname[0], you enclose it in $().
This is called "variable expansion" (other languages, like C#, call it "string interpolation"). Here's a good article that describes it in more detail: https://powershellexplained.com/2017-01-13-powershell-variable-substitution-in-strings/
i just saw the answers and a minute before i realized that i should actually set it up as another variable, $logon = $fname+lname[0]
and pass it as -userPrincipalName $logon.
Thanks for the help, you guy are the best!

Run PowerShell custom function in new window

Function Check-PC
{
$PC = Read-Host "PC Name"
If($PC -eq "exit"){EXIT}
Else{
Write-Host "Pinging $PC to confirm status..."
PING -n 1 $PC
}
This is a snippet of a function I have written into a PowerShell script. I would like the function to run in a new instance of PowerShell, not in the main window.
Is it possible to run this in a separate process of PowerShell without writing it as a separate script and calling the script? Something like this:
$x= Start-Process powershell | Check-PC
I need to keep everything in one script if possible.
Note: It is the involvement of Start-Process that complicates the solution significantly - see below. If powershell were invoked directly from PowerShell, you could safely pass a script block as follows:
powershell ${function:Check-PC} # !! Does NOT work with Start-Process
${function:Check-PC} is an instance of variable namespace notation: it returns the function's body as a script block ([scriptblock] instance); it is the more concise and faster equivalent of Get-Content Function:Check-PC.
If you needed to pass (positional-only) arguments to the script block, you'd have to append -Args, followed by the arguments as an array (,-separated).
Start-Process solution with an auxiliary self-deleting temporary file:
See the bottom half of this answer to a related question.
Start-Process solution with -EncodedCommand and Base64 encoding:
Start-Process powershell -args '-noprofile', '-noexit', '-EncodedCommand',
([Convert]::ToBase64String(
[Text.Encoding]::Unicode.GetBytes(
(Get-Command -Type Function Check-PC).Definition
)
))
The new powershell instance will not see your current session's definitions (unless they're defined in your profiles), so you must pass the body of your function to it (the source code to execute).
(Get-Command -Type Function Check-PC).Definition returns the body of your function definition as a string.
The string needs escaping, however, in order to be passed to the new Powershell process unmodified.
" instances embedded in the string are stripped, unless they are either represented as \" or tripled (""").
(\" rather than the usual `" is needed to escape double quotes in this case, because PowerShell expects \" when passing a command to the powershell.exe executable.)
Similarly, if the string as a whole or a double-quoted string inside the function body ends in (a non-empty run of) \, that \ would be interpreted as an escape character, so the \ must be doubled.Thanks, PetSerAl.
The most robust way to bypass these quoting (escaping) headaches is to use a Base64-encoded string in combination with the -EncodedCommand parameter:
[Convert]::ToBase64String() creates a Base64-encoded string from a [byte[]] array.
[Text.Encoding]::Unicode.GetBytes() converts the (internally UTF-16 -
"Unicode") string to a [byte[]] array.
Note: To also pass arguments, you have two options:
You can "bake" them into the -EncodedCommand argument, assuming you can call a command to pass them to there - see below, which shows how to define your function as such in the new session, so you can call it by name with arguments.Thanks, Abraham Zinala
The advantage of this approach is that you can pass named arguments this way. The disadvantage is that you are limited to arguments that have string-literal representations.
You can use the (currently undocumented) -EncodedArguments parameter, to which you must similarly pass a Base64-encoded string, albeit based on the CLIXML representation of the array of arguments to pass
The advantage of this approach is that you can pass a wider array of data types, within the limits of the type fidelity that CLIXML serialization can provide - see this answer; the disadvantage is that only positional arguments are supported this way (although you could work around that by passing a hashtable that the target code then uses for splatting with the ultimate target command).
Here's a simplified, self-contained example, which uses Write-Output to echo the (invariably positional) arguments received:
$command = 'Write-Output $args'
$argList = 'foo', 42
Start-Process powershell -args '-noprofile', '-noexit',
'-EncodedCommand',
([Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($command))),
'-EncodedArguments',
([Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes(
[System.Management.Automation.PSSerializer]::Serialize($argList)
)))
In case you want to pass the complete function, so it can be called by name in order to pass arguments as part of the command string, a little more work is needed.
# Simply wrapping the body in `function <name> { ... }` is enough.
$func = (Get-Command -Type Function Check-PC)
$wholeFuncDef = 'Function ' + $func.Name + " {`n" + $func.Definition + "`n}"
Start-Process powershell -args '-noprofile', '-noexit', '-EncodedCommand', `
([Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes("$wholeFuncDef; Check-PC")))
As stated above, you can "bake" any arguments to pass to your function - assuming they can be represented as string literals - into the -EncodedCommand argument, simply by appending them inside the "$wholeFuncDef; Check-PC" string above; e.g.,
"$wholeFuncDef; Check-PC -Foo Bar -Baz Quux"
Start-Process solution with regex-based escaping of the source code to pass:
PetSerAl suggests the following alternative, which uses a regex to perform the escaping.
The solution is more concise, but somewhat mind-bending:
Start-Process powershell -args '-noprofile', '-noexit', '-Command', `
('"' +
((Get-Command -Type Function Check-PC).Definition -replace '"|\\(?=\\*("|$))', '\$&') +
'"')
"|\\(?=\\*("|$)) matches every " instance and every nonempty run of \ chars. - character by character - that directly precedes a " char. or the very end of the string.
\\ is needed in the context of a regex to escape a single, literal \.
(?=\\*("|$)) is a positive look-ahead assertion that matches \ only if followed by " or the end of the string ($), either directly, or with further \ instances in between (\\*). Note that since assertions do not contribute to the match, the \ chars., if there are multiple ones, are still matched one by one.
\$& replaces each matched character with a \ followed by the character itself ($&) - see this answer for the constructs you can use in the replacement string of a -replace expression.
Enclosing the result in "..." ('"' + ... + '"') is needed to prevent whitespace normalization; without it, any run of more than one space char. and/or tab char. would be normalized to a single space, because the entire string wouldn't be recognized as a single argument.
Note that if you were to invoke powershell directly, PowerShell would generally automatically enclose the string in "..." behind the scenes, because it does so for arguments that contain whitespace when calling an external utility (a native command-line application), which is what powershell.exe is - unlike the Start-Process cmdlet.
PetSerAl points out that the automatic double-quoting mechanism is not quite that simple, however (the specific content of the string matters with respect to whether automatic double-quoting is applied), and that the specific behavior changed in v5, and again (slightly) in v6.

powershell add-content contains ""

I have a question about the syntax in powershell for Add-Content. My problem is, I want to add text into a textfile and that this text contains "" which is not working. For example:
Add-Content -Value "c:\Users\Administrator\Desktop\"foobar.exe""
Now the output should look like this:
c:\Users\Administrator\Desktop\"foobar.exe"
and that does not work because of these "".
Is there a way to get these "" in the -value parameter?
You can do it like this
Add-Content -Value "c:\Users\Administrator\Desktop\`"foobar.exe`"" -Path .\AA.TXT
escaping the inner ""
Or using single quote to enclose the double quote
Add-Content -Value 'c:\Users\Administrator\Desktop\"foobar.exe"' -Path .\AA.TXT
You can use a few different tricks:
Include the doubly quoted items within a singly quoted string. This makes PowerShell ignore variables, too. For instance: 'I Said "Something"'
Include the doubly quoted items with a preceding backtick. Backticks are the escape character in PowerShell, like \ in javascript or C, and this will make the " part of the string.
Use a here document . They start like this: #" and must end with a "# at the start of a line. This will still let you use variables within the quotes, and still let regular double quotes be.