PowerShell Hyper-V Add network elements to VM object - powershell

We're gathering system information for a fairly large installation including Hyper-V virtual machines. As part of that effort, we're inventorying the attributes of the VMs, and we'd like to associate the IP address (or first IP address) with the VM.
To that end, we have the following script, which details the items we want to collect. Then, for each element of the group, we would like to add the (first) network adapter information. However, the Add-Member that you see in the middle part of this script seems to have no effect; the script outputs the same whether we include that ForEach or omit it, and I don't understand why. Is it not valid to use Add-Member for this sort of object (there is no error, though...)? Can you point out where this script might be going wrong?
$VMs = Get-VM `
| Select #{Name='Hostname';Expression={$(hostname)}} `
, VMName, VMId, Id `
, AutomaticStartAction, Uptime, OperationalStatus `
, PrimaryOperationalStatus, Status, ReplicationHealth, ReplicationState `
, CheckpointType, VirtualMachineType, VirtualMachineSubType `
, State, HardDrives, MemoryMaximum, MemoryMinimum,MemoryStartup `
, ProcessorCount, Path,SizeOfSystemFiles, ReplicationMode `
, ResourceMeteringEnabled, Version, FibreChannelHostBusAdapters `
, DynamicMemoryEnabled, CreationTime, IsDeleted
ForEach ($VM in $VMs) {
$Adapters=($VM | Get-VMNetworkAdapter)
$firstAdapter = "Yes"
ForEach ($Adapter in $Adapters) {
if ($firstAdapter -eq "Yes") { <# Prevent fail if more than one adapter is present. #>
$ip4 = $Adapter.IPAddresses[0]
$ip6 = $Adapter.IPAddresses[1]
$VM | Add-Member -MemberType NoteProperty -Name Adapter -value $Adapter.Name
$VM | Add-Member -MemberType NoteProperty -Name IPAddress -value $Adapter.IPAddresses[0]
$VM | Add-Member -MemberType NoteProperty -Name IP6 -value $Adapter.IPAddresses[1]
$firstAdapter = "No"
}
}
}
$VMs | ConvertTo-Csv -NoTypeInformation `
| Set-Content -Path \path\to\myfile.csv
By the way, if my newbie-ness shows from the use of ForEach() with a test to ignore all but the first... I'd love to see suggestions for a better way.
Thanks for your time.

This required a restructure of the script. Since Network Adapter information was not passed into Get-VMNetworkAdapter, it produced no output.
This script works:
$VMs = Get-VM
ForEach ($VM in $VMs) {
$Adapters = $VM | Get-VMNetworkAdapter
$firstAdapter = "Yes"
ForEach ($Adapter in $Adapters) {
if ($firstAdapter -eq "Yes") { <# Prevent fail if more than one adapter has IP4. #>
if (-not $Adapter.IPAddresses -eq $null) {
}
else {
$ip4 = $Adapter.IPAddresses[0]
$ip6 = $Adapter.IPAddresses[1]
if (-not [string]::IsNullOrEmpty($ip4) ) {
$VM | Add-Member -MemberType NoteProperty -Name NetAdapter -value $Adapter.Name
$VM | Add-Member -MemberType NoteProperty -Name IPAddress -value $ip4
$VM | Add-Member -MemberType NoteProperty -Name IP6 -value $ip6
$firstAdapter = "No"
}
}
}
}
}
$VMS | Select #{Name='Hostname';Expression={$(hostname)}} `
, VMName, VMId, Id `
, NetAdapter, IPAddress, IP6 `
, AutomaticStartAction, Uptime, OperationalStatus `
, PrimaryOperationalStatus, Status, ReplicationHealth, ReplicationState `
, CheckpointType, VirtualMachineType, VirtualMachineSubType `
, State, HardDrives, MemoryMaximum, MemoryMinimum,MemoryStartup `
, ProcessorCount, Path,SizeOfSystemFiles, ReplicationMode `
, ResourceMeteringEnabled, Version, FibreChannelHostBusAdapters `
, DynamicMemoryEnabled, CreationTime, IsDeleted `
| Select * -ExcludeProperty PSComputerName `
| Select * -ExcludeProperty ComputerName `
| ConvertTo-Csv -NoTypeInformation `
| Set-Content -Path \\gotprmry07\PerfLogs\SystemInfo\$(hostname)-VM-details.csv

Related

How to Capture Cluster info Remotely from a non-Clustered Node

I've been banging my head on this for a few days now, and I just can't figure out the best way to do it. I've got a script where I collect a bunch of data and output it to an html file (using PSWriteHTML Module, which is where the New-HTMLTable at the end comes from).
I've piecemealed the script together over time so I can gather the data from multiple servers at once, and for the most part, it all works great. As I've added data to the script to collect new info, there's a few parts that I just can't get to work right remotely. I know the piecemeal approach has left me with some redundant code, but I'm just trying to make it all work right before I re-write it again to clean it up, so my apologies for its current state.
The following code works great when I run the script from a server in a Windows Cluster, but I want things to work from any server, not necessarily a Cluster Node.
Here's orig code for this section:
try
{
$ClusterIPInfo = Invoke-command -computer $Computer {
Get-Cluster | Get-ClusterResource | %{
$_ | select Name,
#{ Name = "Address"; Expression = { $_ | Get-ClusterParameter -Name Address -ErrorAction SilentlyContinue | select -ExpandProperty Value } },
#{ Name = "SubnetMask"; Expression = { $_ | Get-ClusterParameter -Name SubnetMask -ErrorAction SilentlyContinue | select -ExpandProperty Value } }
}
} | Select -Property * -ExcludeProperty PSComputerName, RunSpaceID, PSShowComputerName
$ClusterResourceInfo = Invoke-command -computer $Computer {
Get-ClusterResource | Select Cluster, Name, State, ResourceType, OwnerGroup, OwnerNode, ID, IsCoreResource, IsNetworkClassResource, IsStorageClassResource | Sort-Object -Property OwnerGroup, Name
} | Select -Property * -ExcludeProperty PSComputerName, RunSpaceID, PSShowComputerName
$ResourceInfo = #()
foreach ($rec in $ClusterResourceInfo)
{
$Owner = (Get-ClusterResource | Sort-Object -Property OwnerGroup, Name | Get-ClusterOwnerNode | %{
$_ | select #{ Name = "Name"; Expression = { $_.ClusterObject } },
#{ Name = "PossibleOwners"; Expression = { $_.OwnerNodes } }
} | Where { $_.Name -eq $rec.Name }).PossibleOwners
$Dependency = (Get-ClusterResource | Sort-Object -Property OwnerGroup, Name | Get-ClusterResourceDependency | %{
$_ | select #{ Name = "Name"; Expression = { $_.Resource } },
#{ Name = "Dependency"; Expression = { $_ | Select-Object -ExpandProperty "DependencyExpression" } }
} | Where { $_.Name -eq $rec.Name }).Dependency
$address = ($ClusterIPInfo | Where { $_.Name -eq $rec.Name }).Address
$subnetmask = ($ClusterIPInfo | Where { $_.Name -eq $rec.Name }).SubnetMask
$recObj = New-Object PSObject
$recObj | Add-Member NoteProperty -Name "Cluster" -Value $rec.Cluster
$recObj | Add-Member NoteProperty -Name "Name" -Value $rec.Name
$recObj | Add-Member NoteProperty -Name "State" -Value $rec.State
$recObj | Add-Member NoteProperty -Name "Resource Type" -Value $rec.ResourceType
$recObj | Add-Member NoteProperty -Name "Owner Group" -Value $rec.OwnerGroup
$recObj | Add-Member NoteProperty -Name "Owner Node" -Value $rec.OwnerNode
$recObj | Add-Member NoteProperty -Name "Possible Owners" -Value $Owner
$recObj | Add-Member NoteProperty -Name "Dependency" -Value $Dependency
$recObj | Add-Member NoteProperty -Name "IP Address" -Value $address
$recObj | Add-Member NoteProperty -Name "Subnet Mask" -Value $subnetmask
$recObj | Add-Member NoteProperty -Name "Is Core Resource" -Value $rec.IsCoreResource
$recObj | Add-Member NoteProperty -Name "Is Network Resource" -Value $rec.IsNetworkClassResource
$recObj | Add-Member NoteProperty -Name "Is Storage Resource" -Value $rec.IsStorageClassResource
$ResourceInfo += $recObj
}
New-HTMLTable -DataTable $ResourceInfo -HideFooter -HideButtons -DisableSearch
The parts that don't work correctly remotely are the Dependency and PossibleOwners. I know the reason it doesn't work is because when the server running the script isn't a Cluster Node, it doesn't recognize the command under the Foreach loop Get-ClusterResource. But I just can't figure out how to make those pass correctly from within the Foreach loop, but still use the info from $ClusterResourceInfo.
I've re-written this a hundred different ways, i.e. make a single Invoke-command with basically one big Get-Cluster variable (couldn't get it to capture the Dependency/PossOwners, always $null), splitting up the Dependency and PossOwners to their own separate Invoke-Command (best I can get it to do is display System.Object[], or when I did get it to display, it captured ALL of the Dependencies for all objects and displayed on every line instead of splitting it up correctly).
I've tried every possible way I can think of or found online, but just can't get it to work correctly remotely.
Here's how the orig code above outputs (which is what I want, but I just want to fix it so it works remotely):
I am desperately hoping for some brilliance or guidance to set me on the right track. I tried so many ways, but just never quite got it where it needs to be, so any help is most appreciated and welcome. Thanks.
Couple of things i can suggest.
The "Get-ClusterResource" cmdlet fails because it is not installed on the server.
You may try to load the Failover cluster module using Import-Module, and if it fails (on a non-cluster Node), you can add the Failover Cluster Module for Windows PowerShell Feature, using the following PowerShell cmd:
Add-WindowsFeature RSAT-Clustering-PowerShell
You may try connecting to to the remote cluster node where the resource is hosted, using WMI ?
You have enough info about the resource to be able to write a filtered WMI query.
So the piecemeal approach is what got me in trouble. In trying to merge things together, I kept breaking it (mainly because I think had doubled up the %{}). So instead of merging, I just replicated the parts that were already working as intended.
Ultimately this code worked fine:
$ClusterInfo = Invoke-command -computer $Computer {
Get-Cluster | Get-ClusterResource | %{
$_ | select Name,
#{ Name = "Address"; Expression = { $_ | Get-ClusterParameter -Name Address -ErrorAction SilentlyContinue | select -ExpandProperty Value } },
#{ Name = "SubnetMask"; Expression = { $_ | Get-ClusterParameter -Name SubnetMask -ErrorAction SilentlyContinue | select -ExpandProperty Value } },
#{ Name = "PossibleOwners"; Expression = { $_ | Get-ClusterOwnerNode | select OwnerNodes | select -ExpandProperty OwnerNodes } },
#{ Name = "Dependency"; Expression = { $_ | Get-ClusterResourceDependency | select -ExpandProperty "DependencyExpression" } }
}
} | Select -Property * -ExcludeProperty PSComputerName, RunSpaceID, PSShowComputerName
$ClusterResourceInfo = Invoke-command -computer $Computer {
Get-ClusterResource | Select Cluster, Name, State, ResourceType, OwnerGroup, OwnerNode, IsCoreResource, IsNetworkClassResource, IsStorageClassResource | Sort-Object -Property OwnerGroup, Name
} | Select -Property * -ExcludeProperty PSComputerName, RunSpaceID, PSShowComputerName
$ResourceInfo = #()
foreach ($rec in $ClusterResourceInfo)
{
$Owner = ($ClusterInfo | Where { $_.Name -eq $rec.Name }).PossibleOwners
$Dependency = ($ClusterInfo | Where { $_.Name -eq $rec.Name }).Dependency
$address = ($ClusterInfo | Where { $_.Name -eq $rec.Name }).Address
$subnetmask = ($ClusterInfo | Where { $_.Name -eq $rec.Name }).SubnetMask
$recObj = New-Object PSObject
$recObj | Add-Member NoteProperty -Name "Cluster" -Value $rec.Cluster
$recObj | Add-Member NoteProperty -Name "Name" -Value $rec.Name
$recObj | Add-Member NoteProperty -Name "State" -Value $rec.State
$recObj | Add-Member NoteProperty -Name "Resource Type" -Value $rec.ResourceType
$recObj | Add-Member NoteProperty -Name "Owner Group" -Value $rec.OwnerGroup
$recObj | Add-Member NoteProperty -Name "Owner Node" -Value $rec.OwnerNode
$recObj | Add-Member NoteProperty -Name "Possible Owners" -Value $Owner
$recObj | Add-Member NoteProperty -Name "Dependency" -Value $Dependency
$recObj | Add-Member NoteProperty -Name "IP Address" -Value $address
$recObj | Add-Member NoteProperty -Name "Subnet Mask" -Value $subnetmask
$recObj | Add-Member NoteProperty -Name "Is Core Resource" -Value $rec.IsCoreResource
$recObj | Add-Member NoteProperty -Name "Is Network Resource" -Value $rec.IsNetworkClassResource
$recObj | Add-Member NoteProperty -Name "Is Storage Resource" -Value $rec.IsStorageClassResource
$ResourceInfo += $recObj
}
New-HTMLTable -DataTable $ResourceInfo -HideFooter -HideButtons -DisableSearch

Issue with foreach loop (Combining Commands)

The script below works out great for identifying licensing for each individual host across multiple vCenters. What I am trying to include is the tag for each host as well. When I run the command individually it works fine, however when I run it as part of the code it is not functioning correctly. I highlighted the section if anyone can please take a look thanks. The line of code with the issue is commented out within the script below.
I attempted pushing this into a variable outside and insideof the foreach loop but I am receiving either 0 output, or the same output across each object.
Below is the actual command I put inside the foreach loop which is not functional.
(Get-VMhost | where{$_.Category -like "*Host*"})
$sw = [Diagnostics.Stopwatch]::StartNew()
# Declare our list of vCenters
[array]$vclistall = "vcenter01"
# Ensure were not connected to any vcenters
if ($DefaultVIServer.Count -gt 0) {
Disconnect-VIServer * -Confirm:$false -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Force:$true > $null
}
[array]$report = $null
foreach ($ScriptVCInstance in $vclistall) {
$connection = Connect-VIServer $ScriptVCInstance -ErrorAction SilentlyContinue
if ($connection) {
Write-Host "Collecting License Assets on vCenter $($ScriptVCInstance)"
# Get the license manager assets
$LicenseManager = Get-view LicenseManager
$LicenseAssignmentManager = Get-View $LicenseManager.LicenseAssignmentManager
$licenses = $LicenseAssignmentManager.GetType().GetMethod("QueryAssignedLicenses").Invoke($LicenseAssignmentManager, #($null))
#Format the asset into an object
foreach ($license in $Licenses) {
$object = New-Object -TypeName PSObject
$object | Add-Member -MemberType NoteProperty -Name "vCenter" -Value $($connection.name)
$object | Add-Member -MemberType NoteProperty -Name "Entity" -Value $($license.EntityDisplayName)
$object | Add-Member -MemberType NoteProperty -Name "Display Name" -Value $($license.Properties | where{$_.Key -eq 'ProductName'} | select -ExpandProperty Value)
$object | Add-Member -MemberType NoteProperty -Name "Product Version" -Calue $($License.Properties | where{$_.Key -eq 'FileVersion'} | select -ExpandProperty Value)
$object | Add-Member -MemberType NoteProperty -Name "License" -Value $($license.AssignedLicense.LicenseKey)
$object | Add-Member -MemberType NoteProperty -Name "License Name" -Value $($license.AssignedLicense.Name)
$object | Add-Member -MemberType NoteProperty -Name "Cost Unit" -Value $($license.Properties | where{$_.Key -eq 'CostUnit'} | select -ExpandProperty Value)
$object | Add-Member -MemberType NoteProperty -Name "Used License" -Value $($license.Properties | where{$_.Key -eq 'EntityCost'} | select -ExpandProperty Value)
$object | Add-Member -MemberType NoteProperty -Name "Total Licenses" -Value $($license.AssignedLicense.Total)
# Issue--> $object | Add-Member -MemberType NoteProperty -Name "Tag" -Value $(Get-VMhost | where{$_.Category -like "*Host*"})
$report += $object
if ($DefaultVIServer.Count -gt 0) {
Disconnect-VIServer * -Confirm:$false -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Force:$true > $null
}
} #end foreach $license
} else { # Else for if $connection
Write-warning "Not connected to vCenter $($ScriptVCInstance)"
} # endif $connection
} # End foreach $ScriptVCInstance
# write-out as a CSV file
Write-host "Exporting CSV $($env:USERPROFILE)\Licensed-Assets.csv"
$report | Sort-object "vCenter","License","Entity" | Export-csv "$($env:USERPROFILE)\Licensed-Assets.csv" -NoTypeInformation -UseCulture
$sw.Stop()
$sw.Elapsed

esxi detailed Information

I want to get all the information from ESXi. I extracted the information in different CSV files, but once I want to merge them, it does not show all. But I would rather to create foreach to gather same information.
Add-PsSnapin VMware.VimAutomation.Core -ErrorAction "SilentlyContinue"
Import-Module ‚C:\Program Files\Microsoft Virtual Machine Converter\MvmcCmdlet.psd1‘
$datetime = Get-Date -Format "ddMMyyyy";
# Configuration Block
$User =
$Password =
$ESXiServer = "172.17.1.171"
# Connect to ESXi
$PWD = ConvertTo-SecureString -AsPlainText -Force -String $Password;
$SourceCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User,$PWD;
$sourceConnection = New-MvmcSourceConnection -Server $ESXiServer -SourceCredential $sourceCredential
$SourceVMName = (Get-MvmcSourceVirtualMachine -SourceConnection $sourceconnection).Name
$Datacenter = Get-Datacenter
$Datastore = Get-Datastore
$DataStoreLocation = $Datastore.ExtensionData.info.url
$Datastore = Get-Datastore
# Get-VMHostNetworkAdapter | fl *
Get-VMHostNetworkAdapter | select VMhost, Name, IP, SubnetMask, Mac, DHCPEnabled, DeviceName | Export-Csv C:\VMHostNetworkDetails_$datetime.csv -Delimiter ";"
Get-MvmcSourceVirtualMachine -SourceConnection $sourceconnection | select MemorySizeBytes, OperatingSystem, UsedSpacebytes | Export-Csv C:\VMRAmDetails_$datetime.csv -Delimiter ";"
$Global:DefaultVIServers | Select ProductLine,Version,Build, Port | Export-Csv C:\GlobalDetails_$datetime.csv -Delimiter ";"
#(Import-Csv C:\VMHostNetworkDetails_$datetime.csv) + #(Import-Csv C:\VMRAmDetails_$datetime.csv) + #(Import-Csv C:\GlobalDetails_$datetime.csv) | Export-Csv C:\ESxiDetails_$datetime.csv -Delimiter ";"
Note: get-vm does not work for me.
EDIT:
I tried to get the info by using foreach loop, but cannot get IP, SubnetMask, Mac, DHCPEnabled.$VMSysInfo.IPAddressdoes not give me any IP, but Get-VMHostNetworkAdapter | select VMhost, Name, IP, SubnetMask, Mac, DHCPEnabled, DeviceName gives me IP.
$VmInfo = vmware.vimautomation.core\Get-VM
$VMS = ($VmInfo).Name
$VCenter = #()
foreach ($VM in $VMS)
{
$HostServer = (($VmInfo | ? {$_.Name -eq $VM}).Host).Name
$VMSysInfo = Get-VMGuest -VM $VM
$MyObject = New-Object PSObject -Property #{
VMName = $VM
#VMHostName = $VMSysInfo.HostName
VMIP = $VMSysInfo.IPAddress
VMInstalledOS = $VMSysInfo.OSFullName
PowerState = ($VmInfo | ? {$_.Name -eq $VM}).PowerState
NumberOfCPU = ($VmInfo | ? {$_.Name -eq $VM}).NumCpu
MemoryGB = (($VmInfo | ? {$_.Name -eq $VM}).MemoryMB/1024)
VMDataS = (Get-Datastore -VM $VM).Name
#HostServer = (($VmInfo | ? {$_.Name -eq $VM}).Host).Name
#HostCluster = (Get-Cluster -VMHost $HostServer).Name
Datacenter = (Get-Datacenter -VM $vm).Name
#Notes = $vm | Select -ExpandProperty Description
Portgroup = (Get-VirtualPortGroup -VM $vm).Name
}
$VCenter += $MyObject
}
$VCenter | Select VMName,
#{N='VMIPAddress';E={$_.VMIP -join '; '}},
VMInstalledOS, PowerState, NumberOfCPU, MemoryGB,
#{N='VMDataStore';E={$_.VMDataS -join '; '}},
HostServer, HostCluster,Datacenter, Notes, Portgroup |
Export-Csv C:\test.csv -NoTypeInformation -Delimiter ";"
I changed the foreach loop and it works:
$VmInfo = vmware.vimautomation.core\Get-VM
#$VmInfo = (Get-MvmcSourceVirtualMachine -SourceConnection $sourceconnection).Name
$VMS = ($VmInfo).Name
$Data = #()
foreach ($VM in $VMS)
{
$Datacenter = Get-Datacenter
$Datastore = Get-Datastore
$SourceIP = ($global:DefaultVIServer).name
$DataStoreLocation = $Datastore.ExtensionData.info.url
$VMNetwork = Get-VMHostNetworkAdapter
$PortalGroup = Get-VirtualPortGroup -VM $vm
$MvmcSourceVirtualMachine = Get-MvmcSourceVirtualMachine -SourceConnection $sourceconnection
$VMCustom = New-Object System.Object
$VMCustom | Add-Member -Type NoteProperty -Name DataCenter -Value $Datacenter.Name
$VMCustom | Add-Member -Type NoteProperty -Name DataStoreName -Value $Datastore.Name
$VMCustom | Add-Member -Type NoteProperty -Name DataStoreLocation -Value $DataStoreLocation
$VMCustom | Add-Member -Type NoteProperty -Name NumberOfCPU -Value $VmInfo.NumCpu
$VMCustom | Add-Member -Type NoteProperty -Name PowerState -Value $VmInfo.PowerState
$VMCustom | Add-Member -Type NoteProperty -Name MemoryGB -Value $VmInfo.MemoryGB
$VMCustom | Add-Member -Type NoteProperty -Name VMHost -Value $VMNetwork.VMhost
$VMCustom | Add-Member -Type NoteProperty -Name DHCP -Value $VMNetwork.DHCPEnabled
$VMCustom | Add-Member -Type NoteProperty -Name SubnetMask -Value $VMNetwork.SubnetMask
$VMCustom | Add-Member -Type NoteProperty -Name Client -Value $VMNetwork.Client
$VMCustom | Add-Member -Type NoteProperty -Name IP -Value $SourceIP
$VMCustom | Add-Member -Type NoteProperty -Name MacAddress -Value $VMNetwork.Mac
$VMCustom | Add-Member -Type NoteProperty -Name PortalGroupName -Value $PortalGroup.Name
$VMCustom | Add-Member -Type NoteProperty -Name OperatingSystem -Value $MvmcSourceVirtualMachine.OperatingSystem
$Data += $VMCustom
}
$Data | Export-CSV "C:\ESXiInfo.csv" -Delimiter ";" -NoTypeInformation

Looping through each NoteProperty in a custom object

I'm creating a PSObject in Powershell like this:
$NetworkInfo = New-Object PSObject
$NetworkInfo | Add-Member -type NoteProperty -Name IPAddress -Value "10.10.8.11"
$NetworkInfo | Add-Member -type NoteProperty -Name SubnetMask -Value "255.255.255.0"
$NetworkInfo | Add-Member -type NoteProperty -Name Gateway -Value "10.10.8.254"
$NetworkInfo | Add-Member -type NoteProperty -Name Network -Value "10.10.8.0"
$NetworkInfo | Add-Member -type NoteProperty -Name DNS1 -Value "10.10.10.200"
$NetworkInfo | Add-Member -type NoteProperty -Name DNS2 -Value "10.10.10.201"
$NetworkInfo
I want to do a specific action for each NoteProperty in this PSObject, but I'm not sure how to go about doing that. For example (even though I know it's the wrong way to do it) this code:
ForEach ($noteProperty in $NetworkInfo) {
Write-Host $noteProperty
}
Only outputs the entire object as an array:
#{IPAddress=10.10.8.11; SubnetMask=255.255.255.0; Gateway=10.10.8.254; Network=10.10.8.0; DNS1=10.10.10.200; DNS2=10.10.10.201}
EDIT: Each NoteProperty is of the variable type System.String - if that's pertinent.
Am I creating the object incorrectly, or do I just not understand how to go through the object?
You can use Get-Member as mentioned by Mike; an alternative that keeps the order is to use the PSObject property that is present on all objects:
#View details for all properties on a specific object
$NetworkInfo.PSObject.Properties
#similar code to Mike's, working with name and value:
$NetworkInfo.PSObject.Properties | foreach-object {
$name = $_.Name
$value = $_.value
"$name = $value"
}
This should get you what you need:
$NetworkInfo | get-member -type NoteProperty | foreach-object {
$name=$_.Name ;
$value=$NetworkInfo."$($_.Name)"
write-host "$name = $value"
}
Try:
ForEach ($noteProperty in $NetworkInfo.PSObject.Properties)
{
Write-Host $noteProperty.Name
Write-Host $noteProperty.Value
}
Short (even Faster) version:
$NetworkInfo.psobject.properties.foreach({"$($_.Name) = $($_.Value)"})
Output:
IPAddress = 10.10.8.11
SubnetMask = 255.255.255.0
Gateway = 10.10.8.254
Network = 10.10.8.0
DNS1 = 10.10.10.200
DNS2 = 10.10.10.201
Or another one:
$NetworkInfo.foreach({$_})
Will give you:
IPAddress : 10.10.8.11
SubnetMask : 255.255.255.0
Gateway : 10.10.8.254
Network : 10.10.8.0
DNS1 : 10.10.10.200
DNS2 : 10.10.10.201
You can also select 1 property like:
$NetworkInfo.DNS1
Which will give you:
10.10.10.200
Or select all DNS entries:
$NetworkInfo.foreach({$_}) | Select-Object DNS*
Result:
DNS1 DNS2
---- ----
10.10.10.200 10.10.10.201
Or if you wish:
$NetworkInfo.psobject.properties.where({$_.Name -like 'DNS*' }).Value
Will give:
10.10.10.200
10.10.10.201
Combining foreach and where:
$NetworkInfo.psobject.properties.where({$_.Name -like 'DNS*' }).foreach({
"$($_.Name) = $($_.Value)"})
Wil give:
DNS1 = 10.10.10.200
DNS2 = 10.10.10.201
Transform it into a 2 dimensional array:
$NetworkInfo.psobject.properties.where({$_.Name -like 'DNS*' }).foreach({
,(#([string]$_.Name, [string]$_.Value))})
Do NOT forget the "," in "{,"
Output:
DNS1
10.10.10.200
DNS2
10.10.10.201
Which is two dimensional
$NetworkInfo.psobject.properties.where({$_.Name -like 'DNS*' }).foreach({
,(#([string]$_.Name, [string]$_.Value)) })[0]
DNS1
10.10.10.200
And the following:
$NetworkInfo.psobject.properties.where({$_.Name -like 'DNS*' }).foreach({
,(#([string]$_.Name, [string]$_.Value)) })[0][0]
DNS1
And...
$NetworkInfo.psobject.properties.where({$_.Name -like 'DNS*' }).foreach({
,(#([string]$_.Name, [string]$_.Value)) })[0][1]
10.10.10.200

Powershell script to see currently logged in users (domain and machine) + status (active, idle, away)

I am searching for a simple command to see logged on users on server.
I know this one :
Get-WmiObject -Class win32_computersystem
but this will not provide me the info I need.
It returns :
domain
Manufactureer
Model
Name (Machine name)
PrimaryOwnerName
TotalPhysicalMemory
I run Powershell 3.0 on a Windows 2012 server.
Also
Get-WmiObject Win32_LoggedOnUser -ComputerName $Computer | Select Antecedent -Unique
gives me not the exact answers I need.
I would love to see as well the idle time, or if they are active or away.
In search of this same solution, I found what I needed under a different question in stackoverflow:
Powershell-log-off-remote-session. The below one line will return a list of logged on users.
query user /server:$SERVER
Since we're in the PowerShell area, it's extra useful if we can return a proper PowerShell object ...
I personally like this method of parsing, for the terseness:
((quser) -replace '^>', '') -replace '\s{2,}', ',' | ConvertFrom-Csv
Note: this doesn't account for disconnected ("disc") users, but works well if you just want to get a quick list of users and don't care about the rest of the information. I just wanted a list and didn't care if they were currently disconnected.
If you do care about the rest of the data it's just a little more complex:
(((quser) -replace '^>', '') -replace '\s{2,}', ',').Trim() | ForEach-Object {
if ($_.Split(',').Count -eq 5) {
Write-Output ($_ -replace '(^[^,]+)', '$1,')
} else {
Write-Output $_
}
} | ConvertFrom-Csv
I take it a step farther and give you a very clean object on my blog.
I ended up making this into a module.
There's no "simple command" to do that. You can write a function, or take your choice of several that are available online in various code repositories. I use this:
function get-loggedonuser ($computername){
#mjolinor 3/17/10
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(gwmi win32_logonsession -ComputerName $computername)
$logon_users = #(gwmi win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |% {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |%{
$starttime = [management.managementdatetimeconverter]::todatetime($_.starttime)
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
Maybe you can do something with
get-process -includeusername
If you want to find interactively logged on users, I found a great tip here :https://p0w3rsh3ll.wordpress.com/2012/02/03/get-logged-on-users/ (Win32_ComputerSystem did not help me)
$explorerprocesses = #(Get-WmiObject -Query "Select * FROM Win32_Process WHERE Name='explorer.exe'" -ErrorAction SilentlyContinue)
If ($explorerprocesses.Count -eq 0)
{
"No explorer process found / Nobody interactively logged on"
}
Else
{
ForEach ($i in $explorerprocesses)
{
$Username = $i.GetOwner().User
$Domain = $i.GetOwner().Domain
Write-Host "$Domain\$Username logged on since: $($i.ConvertToDateTime($i.CreationDate))"
}
}
Here is my Approach based on DarKalimHero's Suggestion by selecting only on Explorer.exe processes
Function Get-RdpSessions
{
param(
[string]$computername
)
$processinfo = Get-WmiObject -Query "select * from win32_process where name='explorer.exe'" -ComputerName $computername
$processinfo | ForEach-Object { $_.GetOwner().User } | Sort-Object -Unique | ForEach-Object { New-Object psobject -Property #{Computer=$computername;LoggedOn=$_} } | Select-Object Computer,LoggedOn
}
Another solution, also based on query user, but can handle variations in culture (as far as I can tell) and produces strongly-typed results (i.e. TimeSpan and DateTime values):
# Invoke "query user", it produces an output similar to this, but might be culture-dependant!
#
# USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
# >jantje rdp-tcp#55 2 Active . 3/29/2021 4:24 PM
# pietje 4 Disc 49+01:01 4/14/2021 9:26 AM
$result = (&query 'user' | Out-String -Stream)
# Take the header text and insert a '|' before the start of every HEADER - although defined as inserting a bar after
# every 2 or more spaces, or after the space at the start.
$fencedHeader = $result[0] -replace '(^\s|\s{2,})', '$1|'
# Now get the positions of all bars.
$fenceIndexes = ($fencedHeader | Select-String '\|' -AllMatches).Matches.Index
$timeSpanFormats = [string[]]#("d\+hh\:mm", "h\:mm", "m")
$entries = foreach($line in $result | Select-Object -Skip 1)
{
# Insert bars on the same positions, and then split the line into separate parts using these bars.
$fenceIndexes | ForEach-Object { $line = $line.Insert($_, "|") }
$parts = $line -split '\|' | ForEach-Object { $_.Trim() }
# Parse each part as a strongly typed value, using the UI Culture if needed.
[PSCustomObject] #{
IsCurrent = ($parts[0] -eq '>');
Username = $parts[1];
SessionName = $parts[2];
Id = [int]($parts[3]);
State = $parts[4];
IdleTime = $(if($parts[5] -ne '.') { [TimeSpan]::ParseExact($parts[5], $timeSpanFormats, [CultureInfo]::CurrentUICulture) } else { [TimeSpan]::Zero });
LogonTime = [DateTime]::ParseExact($parts[6], "g", [CultureInfo]::CurrentUICulture);
}
}
# Yields the following result:
#
# IsCurrent Username SessionName Id State IdleTime LogonTime
# --------- -------- ----------- -- ----- -------- ---------
# True jantje rdp-tcp#32 2 Active 00:00:00 3/29/2021 4:24:00 PM
# False pietje 4 Disc 48.11:06:00 4/14/2021 9:26:00 AM
$entries | Format-Table -AutoSize
Team!
I have pretty nice solution to get local session as [PSObject].
Function Get-LocalSession {
<#
.DESCRIPTION
Get local session. Pasre output of command - 'query session'.
#>
[OutputType([PSObject[]])]
[CmdletBinding()]
Param(
)
try {
#region functions
#endregion
$Result = #()
$Output = . query.exe 'session' | select-object -skip 1
#use regex to parse
$pattern = '^(?<This>.)(?<SessionName>[^\s]*)\s*(?<UserName>[a-z]\w*)?\s*(?<Id>[0-9]*)\s*(?<State>\w*)\s*((?<Type>\w*)\s*)?(?<Device>\w*)?'
foreach ( $line in $output ){
$match = [regex]::Matches( $line, $pattern )
if ( $match ){
$PSO = [PSCustomObject]#{
This = $match[0].groups['This'].Value
SessionName = $match[0].groups['SessionName'].Value
UserName = $match[0].groups['UserName'].Value
Id = $match[0].groups['Id'].Value
State = $match[0].groups['State'].Value
Type = $match[0].groups['Type'].Value
Device = $match[0].groups['Device'].Value
}
$Result += $PSO
}
Else {
write-host "Unable to process line [$line] in function [Get-LocalSession]!"
}
}
}
catch {
#Get-ErrorReporting -Trap $PSItem
write-host $PSItem
}
return $Result
}
#Run it
$SessionObject = Get-LocalSession
$SessionObject | format-table -autosize -property *
I have edited mjolinor script to remove duplicate records, and dummy account names such as system, network services,...etc
If you want to get all users
function get-loggedonuser ($computername){
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(gwmi win32_logonsession -ComputerName $computername)
$logon_users = #(gwmi win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |% {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |%{
$starttime = [management.managementdatetimeconverter]::todatetime($_.starttime)
if ($session_user[$_.logonid] -notin $loggedonuser.user -and $session_user[$_.logonid] -notlike "*$*"){
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
}
if you want to have only domain users
function get-loggedonuser ($computername){
$HST= hostname
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
$logontype = #{
"0"="Local System"
"2"="Interactive" #(Local logon)
"3"="Network" # (Remote logon)
"4"="Batch" # (Scheduled task)
"5"="Service" # (Service account logon)
"7"="Unlock" #(Screen saver)
"8"="NetworkCleartext" # (Cleartext network logon)
"9"="NewCredentials" #(RunAs using alternate credentials)
"10"="RemoteInteractive" #(RDP\TS\RemoteAssistance)
"11"="CachedInteractive" #(Local w\cached credentials)
}
$logon_sessions = #(Get-WmiObject win32_logonsession -ComputerName $computername)
$logon_users = #(Get-WmiObject win32_loggedonuser -ComputerName $computername)
$session_user = #{}
$logon_users |ForEach-Object {
$_.antecedent -match $regexa > $nul
$username = $matches[1] + "\" + $matches[2]
$_.dependent -match $regexd > $nul
$session = $matches[1]
$session_user[$session] += $username
}
$logon_sessions |ForEach-Object{
if ($session_user[$_.logonid] -notin $loggedonuser.user -and $session_user[$_.logonid] -notlike "*$*" -and $session_user[$_.logonid] -notlike "*$HST*"){
$loggedonuser = New-Object -TypeName psobject
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Session" -Value $_.logonid
$loggedonuser | Add-Member -MemberType NoteProperty -Name "User" -Value $session_user[$_.logonid]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Type" -Value $logontype[$_.logontype.tostring()]
$loggedonuser | Add-Member -MemberType NoteProperty -Name "Auth" -Value $_.authenticationpackage
$loggedonuser | Add-Member -MemberType NoteProperty -Name "StartTime" -Value $starttime
$loggedonuser
}
}
}
This is what I just figured out and works out great!
Get-Process -IncludeUserName | Select-Object -Unique | Where-Object {$_.UserName -notlike 'NT AUTHORITY\SYSTEM' -and $_.UserName -notlike 'NT AUTHORITY\NETWORK SERVICE' -and $_.UserName -notlike 'NT AUTHORITY\LOCAL SERVICE'} | Format-Table -Wrap -AutoSize