Help: if something equals a certain value, change value to 'x'

Hopefully my title isn’t too vague. I’ve put together a simple script that does a WMI query on anything in the domain with a “Server” OS, and then checks to see if it is physical or virtual. It outputs to a CSV file, and I can sort by the ‘version’ column to determine if it’s physical or virtual. Here’s a sample of the code:

$servers = get-adcomputer -filter 'OperatingSystem -like "*Server*"' -searchbase "DC=domain,DC=local" | select -ExpandProperty Name
 
$results = foreach ($server in $servers ) {gwmi win32_bios -computer $server -Credential $creds  | select PSComputerName,SerialNumber,Version,@{l='IPAddress';e={ (test-connection $server -count 1).ipv4address} }  }
 
$results | export-csv C:\servers\Physical_Servers.csv -NoTypeInformation

What I’d like to accomplish is to have it write something simple like “Physical” if the version doesn’t match the conditions for VMware or Hyper-V, or “Virtual” if it does. I run this to just get physical servers:

$servers = get-adcomputer -filter 'OperatingSystem -like "*Server*"' -searchbase "DC=domain,DC=local" | select -ExpandProperty Name
 
$results = foreach ($server in $servers ) {gwmi win32_bios -computer $server -Credential $creds | Where-Object { $_.SerialNumber -notlike "*VMware*" -and $_.Version -notlike "*VRTUAL*" }  | select PSComputerName,SerialNumber,Version,@{l='IPAddress';e={ (test-connection $server -count 1).ipv4address} }  }
 
$results | export-csv C:\servers\Physical_Servers.csv -NoTypeInformation

This works, but I’d like to do it as listed in the first example and just get a simple output that lists server name and whether it is physical or virtual. Any ideas?

Thanks!

So, the trick is just to add another custom property to your Select-Object property list. You’ve got one for IPAddress already; just add another. Call it “MachineType” or something. In the expression portion, put all your logic. That can include an entire sub-script, with if constructs and whatever. Whatever you “Write-Output” from the expression will become the value of the “MachineType” property.

$stuff | Select Name,Foo,Bar,@{n='Widget';e={ if ($_ -notin (1,2,3,4,5) ) { Write "Physical" } else { Write $_ } }

BTW, I avoid “l” instead of “label” because it looks a lot like the number 1.

This is exactly what I was looking for. Thanks! I was close… I was just over-thinking it, I guess. Also, good tip about using ‘n’ instead of ‘l’ for custom properties. It’s much easier to read that way.

Thanks again!

Follow up question:

So I’m trying to apply the same logic to a query to determine whether or not SNMP is enabled on all servers running any variation of 2008 Server. This is my code:

$creds = Get-Credential $servers = get-adcomputer -Filter 'OperatingSystem -like "*2008*"' | select -ExpandProperty Name $SNMP = foreach ($server in $servers ) {gwmi -computer $server -Credential $creds -query "select *from win32_ServerFeature where name = 'snmp service' "| select PSComputerName,@{N="SNMPInstalled";e={ if ($_.Name -ne "") {write-output "Yes"} else {write-output "No"} } } } $SNMP | Export-Csv c:\servers\SNMPInstalled.csv -NoTypeInformation

This works, but it’s only returning a value for servers that have SNMP installed. It’s skipping over the ones that don’t have it installed. I suspect that this is because the WMI query is returning a null value, but I thought that my ‘if ($_.Name -ne “”)’ statement would catch that.

Thanks in advance for any insight!

On servers without SNMP, WMI is probably not returning anything. So, there’s nothing to pipe to Select-Object, so it doesn’t execute. WMI isn’t returning NULL, it’s returning nothing. There’s not really a way to handle that in a one-liner - you’d need to capture the WMI query to a variable, and then test it using a standalone if construct to see if it contains an object or not.

One-liners are great, but they don’t really do logical branching well.

Give this a shot:

$creds = Get-Credential

$props = @(
    @{ Name = 'ComputerName'; Expression = { $_.Name } }
    @{ Name = 'SNMPInstalled'; Expression = { (Get-WmiObject -ComputerName $_.Name -Credential $creds -Query "select *from win32_ServerFeature where name = 'snmp service' ") -ne $null } }
)

Get-ADComputer -Filter 'OperatingSystem -like "*2008*"' |
Select-Object -Property $props |
Export-Csv c:\servers\SNMPInstalled.csv -NoTypeInformation

Dave, this worked perfectly! Thank you. I didn’t think to separate the name and WMI query out into two different components like you’re doing here. Thanks again!

Dave, I have a followup question. Again, this worked, but I’m a bit confused. According to the help files, Get-WMIObject does not accept pipeline input for the ‘computername’ property, but it appears that we’re passing the name through from the ‘get-adcomputer’ cmdlet. How is this working? Doe it behave differently if you store it in an array first?

Thanks!

You’re not piping anything directly to Get-WmiObject, in that example. It’s Select-Object that accepts the pipeline input. When you use a constructed property, the Expression is a script block that gets executed for each of the input objects that Select-Object is processing (referred to by the $_ automatic variable inside the expression), and that script block’s return value is what gets assigned to the resulting property.

Using Select-Object in that way is sort of a nice shorthand for something like this:

$creds = Get-Credential

Get-ADComputer -Filter 'OperatingSystem -like "*2008*"' |
ForEach-Object {
    $computer = $_
    $snmpObject = Get-WmiObject -ComputerName $computer.Name -Credential $creds -Query "select *from win32_ServerFeature where name = 'snmp service' "

    if ($snmpObject -ne $null)
    {
        $isSnmpInstalled = $true
    }
    else
    {
        $isSnmpInstalled = $false
    }

    New-Object psobject -Property @{
        ComputerName = $computer.Name
        SNMPInstalled = $isSnmpInstalled
    }
} |
Export-Csv c:\servers\SNMPInstalled.csv -NoTypeInformation

They both get you PSObjects with the same properties, but I prefer how the pipeline looks when there’s not a big ForEach-Object loop stuck in the middle of it.