Powershell - If Statement Output 2 values

327 Views Asked by At

Iam trying to write a script to notify if a VMware VM Custom attribute has a value or if the value is null. I need the VM name and the Output Value (either Null or Not Null). Here is what I have but doesn't return the accurate information

$vms = Get-VM

foreach ($vm in $vms) {

    $tag = $vm | Get-Annotation -CustomAttribute "Backup"
   
    if ($tag.value -eq '$null'){
        Write-Output "$vm Attribute doesnt have a value"
    }
    else {
        Write-Output "$vm Attribute has a value assigned"
    }
}
3

There are 3 best solutions below

1
Mathias R. Jessen On

Unless you're specifically looking for the literal string value '$null', you probably want to change the comparison to $null -eq $tag.value

You could create a new object with 2 properties:

$vms = Get-VM

foreach ($vm in $vms) {

    $tag = $vm | Get-Annotation -CustomAttribute "Backup"
   
    if ($null -eq $tag.value){
        $result = "$vm Attribute doesnt have a value"
    }
    else {
        $result = Write-Output "$vm Attribute has a value assigned"
    }

    # output object with Name + result
    [pscustomobject]@{
        Name = $vm.Name
        Result = $result
    }
}

Another, perhaps more PowerShell-idiomatic approach would be to create a similar object with the Select-Object cmdlet:

Get-VM |Select-Object Name,@{Name='HasBackupAttribute';Expression={ $null -eq ($_ | Get-Annotation -CustomAttribute "Backup").Value }}
2
postanote On

When looking at dealing with $null comparisons, see this SO Q&A:
In PowerShell, why is $null -lt 0 = $true? Is that reliable?

I do not have VMware at the moment, but in my small Hyper-V lab, running the following delivers the shown results:

Try
{
    (
        Get-VM | 
        Where-Object -Property FloppyDrive -eq $null | 
        Select Name, FloppyDrive -ErrorAction SilentlyContinue
    ).Count
}
Catch { Write-Warning -Message 'No no records returned'}
# Results
<#
4
#>

Try
{
    (
        Get-VM | 
        Where-Object -Property FloppyDrive -ne $null | 
        Select Name, FloppyDrive -ErrorAction SilentlyContinue
    ).Count
}
Catch { Write-Warning -Message 'No no records returned'}
# Results
<#
WARNING: No no records returned
#>

The results are the same using these as well.

Try
{
    (
        Get-VM | 
        Where-Object -Property FloppyDrive -Match $null | 
        Select Name, FloppyDrive -ErrorAction SilentlyContinue
    ).Count
}
Catch { Write-Warning -Message 'No no records returned'}

Try
{
    (
        Get-VM | 
        Where-Object -Property FloppyDrive -NotMatch $null | 
        Select Name, FloppyDrive -ErrorAction SilentlyContinue
    ).Count
}
Catch { Write-Warning -Message 'No no records returned'}

In your use case, try this refactor:

(Get-VM).Name | 
foreach {
    if ((Get-VM -Entity $PSitem | Get-Annotation -CustomAttribute 'Backup') -eq $null)
    { "$PSItem - Attribute doesnt have a value"    }
    else {"$PSItem - Attribute has a value assigned"}
}

Or...

    (Get-VM).Name | 
foreach {
    if ((Get-VM -Entity $PSitem | Get-Annotation -CustomAttribute 'Backup') -Match $null)
    { "$PSItem - Attribute doesnt have a value"    }
    else {"$PSItem - Attribute has a value assigned"}
}
0
AdminOfThings On

Annotation values are strings. When there is no value present, the string is considered empty rather than null. So the $null -eq value test will not yield the desired results. You can simply perform an implicit boolean conversion of the value to determine if it is empty.

$vms = Get-VM
foreach ($vm in $vms) {

    $tag = $vm | Get-Annotation -CustomAttribute "Backup"
    # Empty string returns false. Nonempty string returns true.
    if ($tag.value){
        Write-Output "$vm Attribute has a value assigned"
    }
    else {
        Write-Output "$vm Attribute doesn't have a value"
    }
}

You will discover that $tag.value | Get-Member returns a type System.String. So when value seemingly has no value, it actually is an empty string. You can perform a variety of tests to determine if the value is empty. An empty string value inside of an if statement evaluates to False. See below for some examples, which can all be used inside of if statements.

$Empty = [pscustomobject]@{value = ''}
$NotEmpty = [pscustomobject]@{value = 'My String'}

# Test 1 Using IsNullOrEmpty static method
[string]::IsNullOrEmpty($Empty.value)
True
[string]::IsNullOrEmpty($NotEmpty.value)
False

# Test 2 Using [bool] type accelerator
[bool]$Empty.value
False
[bool]$NotEmpty.value
True

# Test 3 Using length property. Empty string has length of 0.
$Empty.value.length -eq 0
True
$NotEmpty.value.length -eq 0
False

# Test 4 Using if statement. ! in front of an expression negates its boolean value
if ($Empty.value) { "Value is not empty" } else { "Value is empty" }
Value is empty
if ($NotEmpty.value) { "Value is not empty" } else { "Value is empty" }
Value is not empty
if (!$Empty.value) { "Value is empty" }
Value is empty