Powershell Passing Variable to Function - string

I don't understand what is going on...
In the VerifyEmailSettings function, the $AdminEmailAddress is one of many parameters i can pass to the ps command i am using.
I want to be able to pass the paramater name, and value to other functions like below. However, when i pass this along, i get some odd results. As you can see in the results, trying to print the $SettingName in the VerifyEmailSettings function echos AdminEmailAddress admin#superuser.com Verified, Same instead of what i want... AdminEmailAddress Verified, Same The "admin#superuser.com is mixed in there somehow. Same happens with the $SetName in the SetEmailSettings functions.
Thanks in advance!!
Write-Host "Starting Script"
#Assigning Variables
$AdminEmailAddress = "admin#superuser.com"
$SmtpServer = "exchange.local"
$FromEmailAddress = "fsrm#omg.com"
If (GetInstallStatus){
Write-Host "FSRM Installed, Skipping Install"
Write-Host "Checking Email Settings"
VerifyEmailSettings([string]"AdminEmailAddress",[string]$AdminEmailAddress)
} else {
Write-Host "FSRM Not Installed, Installing"
Install-WindowsFeature –Name FS-Resource-Manager –IncludeManagementTools
If (GetInstallStatus){
Write-Host "FSRM Installed"
} else {
Write-Host "FSRM Error on Install, Halting"
#halt here
}
}
function GetInstallStatus {
$status = (Get-WindowsFeature -Name FS-Resource-Manager | ft Installed -autosize -hidetableheaders | out-string).trim();
return $status
}
function VerifyEmailSettings([string]$SettingName, [string]$SettingData) {
$Verify = (Get-FsrmSetting | Select-Object $SettingName | FT -autosize -hidetableheaders | Out-String).Trim()
If ($Verify -eq $SettingData) {
Write-Host $SettingName "Verified, Same"
SetEmailSettings([string]$SettingName, [string]$SettingData)
} Else {
Write-Host $SettingName "Wrong, Updating"
SetEmailSettings([string]$SettingName, [string]$SettingData)
}
}
function SetEmailSettings([string]$SetName, [string]$SetData) {
$SetName
#Set-FsrmSetting $SetName $SetData
}
Here is the results i get:
Starting Script
FSRM Installed, Skipping Install
Checking Email Settings
AdminEmailAddress admin#superuser.com Verified, Same
AdminEmailAddress admin#superuser.com

Do not call PowerShell functions with parentheses and commas
VerifyEmailSettings([string]"AdminEmailAddress",[string]$AdminEmailAddress)
What you're actually doing here is passing an array containing both values as the first argument and nothing for the second argument. That should be written like this:
VerifyEmailSettings "AdminEmailAddress" $AdminEmailAddress
Or
VerifyEmailSettings -SettingName "AdminEmailAddress" -SettingData $AdminEmailAddress
(there is no need to cast your strings as [string])
Use Strict Mode
What you've done is a common error in PowerShell, made more common by the fact that you do use parentheses and commas when calling methods on .Net objects. I still do this once in a while after years of using PowerShell.
You can set strict mode which actually catches this for you and warns you about it:
Set-StrictMode -Version 2.0

Related

PowerShell Regex get multiple substrings between 2 strings and write them to files with sequence numbers

Old thread
My question regards:
function GetStringBetweenTwoStrings($firstString, $secondString, $importPath){
#Get content from file
$file = Get-Content $importPath
#Regex pattern to compare two strings
$pattern = "$firstString(.*?)$secondString"
#Perform the opperation
$result = [regex]::Match($file,$pattern).Groups[1].Value
#Return result
return $result
}
GetStringBetweenTwoStrings -firstString "Lorem" -secondString "is" -importPath "C:\Temp\test.txt"
This is nice for only one -firstString and -secondString, but how to use this function to chronologically write multiple same strings in numbered TXT?
txt - file(with more sections of text):
Lorem
....
is
--> write to 001.txt
Lorem
....
is
--> write to 002.txt
and so forth....
And the structure of the section is preserved and is not in one line.
I hope someone can tell me that. Thanks.
The function you quote has several limitations (I've left feedback on the original answer), most notably only ever reporting one match.
Assuming an improved function named Select-StringBetween (see source code below), you can solve your problem as follows:
$index = #{ value = 0 }
Get-ChildItem C:\Temp\test.txt |
Select-StringBetween -Pattern 'Lorem', 'is' -Inclusive |
Set-Content -LiteralPath { '{0:000}.txt' -f ++$index.Value }
Select-StringBetween source code:
Note: The syntax is in part patterned after Select-String. After defining the function, run Select-StringBetween -? to see its syntax; the parameter names are hopefully self-explanatory.
function Select-StringBetween {
[CmdletBinding(DefaultParameterSetName='String')]
param(
[Parameter(Mandatory, Position=0)]
[ValidateCount(2, 2)]
[string[]] $Patterns,
[Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName='File')]
[Alias('PSPath')]
[string] $LiteralPath,
[Parameter(Mandatory, ValueFromPipeline, ParameterSetName='String')]
[string] $InputObject,
[switch] $Inclusive,
[switch] $SimpleMatch,
[switch] $Trim
)
process {
if ($LiteralPath) {
$InputObject = Get-Content -ErrorAction Stop -Raw -LiteralPath $LiteralPath
}
if ($Inclusive) {
$regex = '(?s)(?:{0}).*?(?:{1})' -f
($Patterns[0], [regex]::Escape($Patterns[0]))[$SimpleMatch.IsPresent],
($Patterns[1], [regex]::Escape($Patterns[1]))[$SimpleMatch.IsPresent]
}
else {
$regex = '(?s)(?<={0}).*?(?={1})' -f
($Patterns[0], [regex]::Escape($Patterns[0]))[$SimpleMatch.IsPresent],
($Patterns[1], [regex]::Escape($Patterns[1]))[$SimpleMatch.IsPresent]
}
if ($Trim) {
[regex]::Matches(
$InputObject,
$regex
).Value.Trim()
}
else {
[regex]::Matches(
$InputObject,
$regex
).Value
}
}
}
Note that there's also a pending feature request on GitHub to add this functionality directly to Select-String - see GitHub issue #15136

Version strings to "System.Version" too long (or short) in PowerShell

How can I force conversion to type System.Version in PowerShell, or more likely, better understand why I cannot arbitrarily assign number strings type System.Version?
We ingest some software updates in folders whose titles include version numbers. In trying to get reports on what the latest versions ingested are, I have been doing the following quick and dirty:
ForEach ($Folder in $(Get-ChildItem -Path $SoftwareDirectory -Directory))
{
$CurrentVersion = $Folder -Replace "[^0-9.]"
If ($CurrentVersion -ne $null)
{
If ([System.Version]$CurrentVersion -gt [System.Version]$MaxVersion)
{
$MaxVersion = $CurrentVersion
$MaxFolder = $Folder
}
}
}
This would be fed directory titles like the following,
foo-tools-1.12.file
bar-static-3.4.0.file
Most of the time, this is acceptable. However, when encountering some oddballs with longer numbers, like the following,
applet-4u331r364.file
In which case, System.Version refuses the resulting string as being too long.
Cannot convert value "4331364" to type "System.Version". Error: "Version string portion was too short or too long."
You need to ensure that your version strings have at least two components in order for a cast to [version] to succeed:
(
#(
'oo-tools-1.12.file'
'bar-static-3.4.0.file'
'applet-4u331r364.file'
) -replace '[^0-9.]'
).TrimEnd('.') -replace '^[^.]+$', '$&.0' | ForEach-Object { [version] $_ }
The above transforms 'applet-4u331r364.file' into '4331364.0', which works when cast to [version].
Note that you can avoid the need for .TrimEnd('.') if you exclude the filename extension to begin with: $Folder.BaseName -replace '[^0-9.]'
-replace '^[^.]+$', '$&.0' matches only strings that contain no . chars., in full, i.e. only those that don't already have at least two components; replacement expression $&.0 appends literal .0 to the matched string ($&).
Output (via Format-Table -AutoSize):
Major Minor Build Revision
----- ----- ----- --------
1 12 -1 -1
3 4 0 -1
4331364 0 -1 -1

Why is this condition being met when verifying parameters using the -notMatch condition?

I am validating parameters passed into a release pipeline with Powershell. The parameter I am passing as a pipeline variable is val1. Here is my code below:
if ("$(Value)" -notMatch "val1" -or "$(Value)" -notMatch "val2"){
Write-Host "$(Value) must be val1 || val2"
Write-Host "Value of param: "$(Value)""
exit 1
}
When I print my value is states val1. Why is this condition being met? I thought perhaps it was because its case sensitive however even when I modify the condition to catch the exact case, it's still being met.
If your variable should have only val1 or val2, you have to use -and instead -or.
if ("$(Value)" -notMatch "val1" -and "$(Value)" -notMatch "val2"){
Write-Host "$(Value) must be val1 || val2"
Write-Host "Value of param: "$(Value)""
exit 1
}
Change it to
if ("$(Value)" -notMatch "val1" -and "$(Value)" -notMatch "val2"){
Write-Host "$(Value) must be val1 || val2"
Write-Host "Value of param: "$(Value)""
exit 1
}
Note: $(Value) in the question as well as in the code below is an Azure pipeline macro, not a PowerShell variable reference such as $Value. Since such a macro is expanded to its value before PowerShell sees it, enclosing it in "..." - again, as in the question and in the code below - ensures that no syntax error occurs if the value happens to contain spaces or other PowerShell metacharacters.
The Shamrai Aleksander's helpful answer explains the logic error, but there may be an additional problem:
-match (and its -notmatch variant), the regular-expression matching operator, matches substrings by default, so that "$(Value)" expanding to aval1, for instance, would mistakenly pass the test as well.
for literal, whole-value comparisons against a collection, you can use the -in operator (note that -contains serves the same purpose only with the operands reversed) and its variants, notably -notin in this case; use -cnotin for case-sensitive comparisons.
# Note: `$(Value)` is an *Azure pipeline macro (variable)*, which is expanded
# *before* PowerShell sees the code.
# In pure PowerShell code, the equivalent would be just `$Value`
if ("$(Value)" -notin 'val1', 'val2') {
Write-Error '$(Value) must be val1 || val2'
exit 1
}
I thought perhaps it was because its case-sensitive however
Note that all PowerShell operators are case-insensitive by default (when they operate on text); PowerShell is case-insensitive by default in most respects.
Case-sensitive operation requires use of the c prefixed variants, such as -cmatch.
Optionally, to signal the intent more clearly, you can signal case-insensitive operation by using the i-prefixed variants, such as -imatch (all i-prefixed variants behave like their non-prefixed base forms).

In PowerShell how do I load multiple values into a variable?

I have a requirement at work to check several registry key values and I want to automate this process using PowerShell. One of the registry keys that I check has 3 values and I am not able to successfully check it using my PowerShell script.
I do not believe I am correctly loading the $value variable so that it can be compared to the value of $path.
$path = (Get-ItemProperty HKLM:\System\CurrentControlSet\Services\LanManServer).NullSessionPipe
$value = “netlogon samr lsarpc”
if ($path -ne $value) {
Write-Host “Value is incorrect or missing.”
} else {
Write–Host “Config is correct.”
}
I expect the output to be
Config is correct.
Instead I get
Value is incorrect or missing.
Using Regedit I can see that the key has the correct values.
NullSessionPipe apparently is a REG_MULTI_SZ, meaning that the data is returned as an array of strings. You could do "$path" -ne $value to mangle the array into a flat string, but that would imply that the order of the substrings is identical in both strings. A better approach is to compare arrays via Compare-Object.
$path = (Get-ItemProperty HKLM:\...).NullSessionPipe
$value = 'netlogon', 'samr', 'lsarpc'
if (Compare-Object $path $value) {
'arrays differ'
} else {
'arrays are equal'
}
#Ansgar, After some experimenting I was able to get the script to work. I deleted the blank spaces and added a carriage return after netlogon and samr. It works perfectly!!!
$path = (Get-ItemProperty HKLM:\System\CurrentControlSet\Services\LanManServer).NullSessionPipe
$value = 'netlogon',
' samr',
' lsarpc'
if (Compare-Object $path $value) {
'arrays differ'
} else {
'arrays are equal'
}

Powershell format operator inside function

I've found out the format operator is working differently inside a function compared to a plain script.
Here's a simple example of what is working as expected:
[string]$name = 'Scripting Guy'
[string]$statement = 'PowerShell rocks'
$s = "The {0} thinks that {1}!" -f $name, $statement
write-host $s
producing:
The Scripting Guy thinks that PowerShell rocks!
While inside a function it does something different:
function myFunc( [string] $iname, [string] $istatement) {
$s = "The {0} thinks that {1}!" -f $iname, $istatement
write-host $s
}
[string]$name = 'Scripting Guy'
[string]$statement = 'PowerShell rocks'
myFunc($name, $statement)
produces:
The Scripting Guy PowerShell rocks thinks that !
I tried to play with it to find out what it's doing:
function myFunc( [string] $iname, [string] $istatement) {
$s = "The {0} thinks that {1}! {2} {3}" -f $iname, $istatement, "=====", $iname
write-host $s
}
[string]$name = 'Scripting Guy'
[string]$statement = 'PowerShell rocks'
myFunc($name, $statement)
This produces:
The Scripting Guy PowerShell rocks thinks that ! ===== Scripting Guy PowerShell rocks
So now I don't know what to think about this.
You should call the function as follows:
myFunc -iname "Scripting Guy" -istatement "Powershell Rocks!!"
or
myFunc $name $statement
The current method you're using passes a single array object that's why the elements get printed in succession

Resources