PowerShell Switch Statement Issue - powershell

I have the following simple code and it isn't working (simplified from a much larger function)
The user in my first example doesn't exist in my use case
The switch statement doesn't work
A break-point (using ISE) on both the statements in the first switch never get triggered
The second example works without issue
The third code snippet is some troubleshooting code to prove $myADObject is null
What am I missing?
Snippet 1:
$user = "no.one"
$myADUsr = Get-ADObject -Filter { sAMAccountName -like $user }
switch ($myADUsr) {
$null { 'User object variable is null' }
default { 'User object variable has a value' }
}
Snippet 2:
$myADUsr = $null
switch ($myADUsr) {
$null { 'The variable is null' }
default { 'The variable has a value' }
}
Snippet 3:
clear-host
$member = "no.one"
$adobject = Get-ADObject -Filter { sAMAccountName -like $member }
'=== Frist switch ==='
switch ($adobject) {
{$null} { "tests as null"}
{$null -eq $_ } { 'another null test' }
{[string]::IsNullOrEmpty($_)} {'string null test'}
{$_ -eq [string]::Empty} { 'another string null test'}
{$null -ne $_ } { 'not null' }
default { "I don't think this is working ..." }
}
'==== if ====='
If ($null -eq $adobject) { 'null' } else { 'not null' }
'==== second switch ==='
$nullvariable = $null
switch ($nullvariable) {
$adobject { 'null object' }
$null { "null"}
default { "not null" }
}

The switch statement implicitly operates on collections (enumerable data types), and evaluates its branches for each element of the enumeration.
A function or cmdlet call that yields no output technically outputs the [System.Management.Automation.Internal.AutomationNull]::Value singleton, which can be conceived of as an array-valued $null - that is, in enumeration contexts such as switch it behaves like an empty collection: there's nothing to enumerate.
Therefore, because $myADUsr in your example contains [System.Management.Automation.Internal.AutomationNull]::Value due to Get-AdUser not producing any output, the switch statement is effectively skipped.
If all you need to know is whether an AD user object was returned, use PowerShell's implicit to-Boolean conversion in an if statement, because in an expression context [System.Management.Automation.Internal.AutomationNull]::Value behaves like $null (and therefore evaluates to $false):
$myADUsr = Get-ADObject -Filter 'sAMAccountName -like $user'
if ($myAdUsr) {
'User object variable has a value'
}
else {
'User object variable is null'
}

It think updating my original snippet #1 like this gets me out of trouble, it seems to work so I can continue to use the switch statement I have already written. I'm still testing.
$user = "no.one"
$myADUsr = Get-ADObject -Filter "sAMAccountName -like '$user'"
if ( #($myADUsr).Count -eq 0 ) { $myADUsr = $null }
switch ($myADUsr) {
$null { 'User object variable is null' }
default { 'User object variable has a value' }
}

Related

How to use dism.exe to check whether Windows Features are turned on/off on Windows 7?

I use Get-WindowsOptionalFeature to check whether the Windows feature is turned on/off successfully, but Windows 7 does not support this instruction.
$IIS_InstallPro = "IIS-WebServerRole","IIS-WebServer","IIS-CommonHttpFeatures","IIS-HttpErrors"
#$features = Get-WindowsOptionalFeature -Online -FeatureName IIS*
$features = Dism /Online /Get-Features
function CheckIIS{
foreach($feature in $features)
{
if($feature.State -eq "Disabled" -and $IIS_InstallPro -match $feature.FeatureNmae)
{
return $False
}
}
return $true
}
CheckIIS
Should I use dism.exe to check and return the result?
Wanted to know if it's good practice to do that and what would be the best way to do that?
No, there is not a way to make those cmdlets work on Windows 7.
While you really shouldn't be using Windows 7 anymore, you should still be able to get this information from WMI. I don't recall if the Get-CimInstnace cmdlets were available on 7, so I'll use the Get-WmiObject method:
Function Get-WmiWindowsOptionalFeatures {
[CmdletBinding()]
Param(
[string]$FeatureName,
[ValidateSet('Enabled', 'Disabled', 'Absent', 'Unknown', '1', '2', '3', '4')]
[string]$InstallState
)
Get-WmiObject Win32_OptionalFeature | Where-Object {
$feature = $_
$featureMatch = !$FeatureName -or ( $FeatureName -and $feature.Name -like $FeatureName )
$installStateMatch = switch ( $InstallState ) {
{ $_ -in 'Enabled', '1' } {
$feature.InstallState -eq 1
break
}
{ $_ -in 'Disabled', '2' } {
$feature.InstallState -eq 2
break
}
{ $_ -in 'Absent', '3' } {
$feature.InstallState -eq 3
break
}
{ $_ -in 'Unknown', '4' } {
$feature.InstallState -eq 4
break
}
default {
$true
break
}
}
$featureMatch -and $installStateMatch
} | Select-Object Name, Caption, Description, InstallDate, #{
Label = 'InstallState'
Expression = {
switch ( $_.InstallState ) {
1 {
'Enabled'
break
}
2 {
'Disabled'
break
}
3 {
'Absent'
break
}
4 {
'Unknown'
break
}
default {
$_.ToString()
break
}
}
}
}
}
This will give you back a nice operable object with the important fields which can be evaluated and operated upon. The class you have to inspect is Win32_OptionalFeatures.
To use the function:
No arguments: returns all features
-FeatureName: returns features matching the Name. Supports -like patterns.
-InstallState: returns features matching the InstallState. Takes convenient strings or the numbered value as mapped below.
To understand the install state, here are the possible values for each (they are stored as a uint32):
Enabled
Disabled
Absent
Unknown
Unfortunately, there is no way to use WMI to install the features, so you'll have to install them with dism.exe.

Strange behaviour of string comparison in Powershell

Consider the following function :
function myfunc()
{
if (condition1) {
return 'nowork'
} elseif (condition2) {
return $false
} elseif (condition3) {
return $true
}
Now if I call this function, and I know that condition3 is true, I can see that True is returned:
...
$result = myfunc
Write-Host $result
(this writes True to the console.)
The next statement in the calling function is an if statement to determine what was returned, and act upon that:
$result = myfunc
Write-Host $result
if ($result -eq 'nowork') {
do this..
} elseif ($result -eq $false) {
do that..
} elseif ($result -eq $true) {
do something else..
}
And this is where it gets strange (to me). Even though I can see that True is returned, the if statement decides to go do 'do this..', the first branch of the if statement, where I would have expected that 'do something else..' would have been done.
Another strange thing is that it sometimes works, sometimes not. I tried changing the if statement to:
if ('nowork' -eq $result)
and then what went wrong first now worked, but later on the same issue re-appeared.
I'm guessing there's something wrong with my first string comparison, but I can't figure out what. I'm used to writing scripts in Linux (bash), so Powershell must be acting differently.
Btw: script is run in Debian 10, Powershell 7, but the exact same problem also appears on a Windows machine with Powershell 5.0.
Please help..
You're comparing apples and oranges
PowerShell's comparison operator behavior depends on type of the left-hand side operand.
When your lhs ($result) is a [bool] (ie. $true or $false), PowerShell will attempt to convert the right-hand side operand to [bool] as well before comparing the two.
Converting a non-empty string (ie. 'nowork') to [bool] results in $true, so the if condition evaluates to $true -eq $true -> $true.
You can fix this by manually type checking:
if($result -is [bool]){
if($result){
# was $true
}
else {
# was $false
}
}
elseif($result -eq 'nowork'){
# was 'nowork'
}
The nicer way of solving this however would be to always return the same type of object. In your case where you have 3 different return options, consider an enum:
enum WorkAmount
{
None
Some
All
}
function myfunc()
{
if (condition1) {
return [WorkAmount]::None
} elseif (condition2) {
return [WorkAmount]::Some
} elseif (condition3) {
return [WorkAmount]::All
}
}

Powershell Switch Condition

I'm having a little trouble with a PS script right now.
What I am trying to do:
Depending on which OU a computer belongs to, it should get a different printer mapped.
I am trying this with a switch condition, but regardless of what I try the condition seems to be always TRUE (although I know it isn't)
When I type in the condition into PowerShell manually, I get the correct values if the condition is TRUE or FALSE. But as soon as I use it in the switch, the condition seems to be always TRUE.
What I have so far:
With dsquery I check if a computer belongs to a specific OU.
If a value is returned, which only happens if the query succeeds, I put it into my $SwitchDump variable (Condition TRUE).
From my understanding, if a device is not found in the OU, there is no value that will be passed to my $SwitchDump variable and hence should be $null right?
But it keeps mapping the printer.
Switch ($SwitchDump = dsquery computer $OU_TO_SEARCH_IN|findstr $env:COMPUTERNAME | Out-String)
{
$SwitchDump -ne $null {Add-Printer -ConnectionName \\$PrintServer\$DesiredPrinter}
}
Or am I just barking up the wrong tree?
Any ideas would be greatly appreciated.
$SwitchDump = dsquery computer $OU_TO_SEARCH_IN | findstr $env:COMPUTERNAME | Out-String
Switch ($SwitchDump)
{
{$_ -ne $null} {Add-Printer -ConnectionName \\$PrintServer\$DesiredPrinter}
}
You need to use $_ to represent the variable being tested by the Switch if you want to do anything beyond simple comparisons for values. You also need to make those comparisons a scriptblock by using { }.
You're assigning the dsquery to $SwitchDump... which will [almost] always return true ;-)
You probably want to perform an equality check i.e.
Switch ($SwitchDump -eq dsquery computer $OU...
Also it looks like you have your switch syntax slightly off: https://ss64.com/ps/switch.html
$SwitchDump = dsquery computer $OU_TO_SEARCH_IN|findstr $env:COMPUTERNAME
if (-not $SwitchDump) {
Write-Output "SwitchDump is empty"
}
switch ($SwitchDump) {
$null { Write-Host "SwitchDump = NULL" ; break; }
"value1" { Write-Host "SwitchDump = value1"; break; }
"value2" { Write-Host "SwitchDump = value2"; break; }
"value3" { Write-Host "SwitchDump = value3"; break; }
default { Write-Host "None of the above" ; break; }
}

Inconsistent behavior in powershell with null parameters

I need to write a function in powershell that tells apart a 'parameter not being passed' from one passed with string empty (or any other string)
I wrote it like this:
function Set-X {
param(
[AllowNull()][string]$MyParam = [System.Management.Automation.Language.NullString]::Value
)
if ($null -ne $MyParam) { write-host 'oops' }
else { write-host 'ok' }
}
If I call Set-X without parameters from ISE, it works as I expect and prints 'ok'.
But if I do that from the normal console, it prints 'oops'.
What is going on? What is the proper way to do it?
Allowing the user to pass in a parameter argument value of $null does not change the fact that powershell will attempt to convert it to a [string].
Converting a $null value in powershell to a string results in an empty string:
$str = [string]$null
$null -eq $str # False
'' -eq $str # True
(same goes for $null -as [string] and "$null")
Remove the type constraint on the MyParam parameter if you not only want to allow $null but also accept $null as a parameter value:
function Set-X {
param(
[AllowNull()]$MyParam = [System.Management.Automation.Language.NullString]::Value
)
if ($null -ne $MyParam) { write-host 'oops' }
else { write-host 'ok' }
}
As Mathias and BenH have written, the culprit is casting $null to the [string] type, which results in an empty string:
[string]$null -eq '' #This is True
But for the sample code in Mathias answer to work correctly we also have to replace
[System.Management.Automation.Language.NullString]::Value
with $null
function Set-X {
param(
[AllowNull()]$MyParam = $null
)
if ($null -ne $MyParam) { write-host 'oops' }
else { write-host 'ok' }
}

Powershell Switch - Multiple Clauses

I'm creating a script that will be updating an Excel spreadsheet depending on conditions.
This is what I currently have:
if ($endIRs -ne $null) {
$endIRs | ForEach-Object {
try {
$classification = $_.Classification
$priority = $_.Priority
$title = $_.Title
$id = $_.Id
switch ($classification) {
{($_ -eq 'Reports') -and ($priority -eq '1')} {
$GeAppsReportSheet.Cells.Item(8,2).Interior.ColorIndex = 3
$GeAppsReportSheet.Cells.Item(8,2) = 'RED'
}
#more switch statements to go here
}
catch {#catch tickets with $classification not listed}
}
}
The $endIRs at the start holds a series of high priority 'incidents' that have been logged in the last 12 hours. If there is none, everything will be 'GREEN' which is set by default.
What I am trying to achieve with the switch statement is if (($classification -eq 'Reports') -and ($priority -eq '1')) {'change the cell colour and text'} which I can do on its own, but I need it to check if the priority is "1" or "2" and do something different against the "Reports" classification cell in the spreadsheet.
Can you do an if statement within the switch statement, or is there a better way to do it?
You can use $true as the switch condition and put the checks as scriptblock values:
switch ($true) {
{($classification -eq 'Reports') -and ($priority -eq '1')} {
...
}
# ...
# more switch statements to go here
# ...
default {
...
}
}
I never really liked this approach, though. Always looked like an ugly hack to me. I'd prefer a if..elseif..else control structure:
if ($classification -eq 'Reports' -and $priority -eq '1') {
...
} elseif (...) {
...
} elseif (...) {
...
} else {
...
}
Edit: Of course you can also use a "regular" switch statement and nest other conditionals in the action scriptblocks:
switch ($classification) {
'Reports' {
if ($priority -eq '1') {
...
} elseif ($priority -eq '2') {
...
}
}
# ...
# more switch statements to go here
# ...
default {
...
}
}