I did a search and I understand the issue that some of these are arrays. What I am not sure I understand is why I am getting this when I am specifically trying to return just one value.
I am using a ForEach loop and pulling from a text file the names of the computers I want to query.
I want to verify that the IP address I am pinging is the same computer I want to connect to. We have many users on VPN and the DNS doesn't update fast enough. This means we get some incorrect information.
$NIC2 = Get-WmiObject win32_NetworkAdapterConfiguration -ComputerName $computer | Where-Object {$_.DNSDomain -eq "usms.contoso.com"} | Select-Object IPAddress
This should give me only one IP address and not an array, but unfortunately I get the System.String[] in the CSV output. If I just type this command out in the PowerShell command line it works beautifully, but not when I put it into a CSV.
any ideas?
Thanks in advance.
Entire code below - I get the results for everything except the $NIC2 and doing $NIC2.IPAddress[0] doesn't seem to work.
Putting $NIC2[0] just give me this in that column
dapterConfiguration -ComputerName $computer | Where-Object {$_.DNSDomain -eq "contoso.com"} | Select-Object IPAddress
#Definitions of variables
#************************
#Defines the location and name of the text file from where computers will be pulled
$Computers = Get-Content "c:\Temp\computers.txt"
#Defines the variable and format for Date
$Date = Get-date -UFormat %h_%d_%y_%H%M
#Define the name of the files to output
$OutputFile = "C:\Temp\Verify-Computer-$Date.csv" #All Computers On-Line Status will be provided in this file
#Defines the Array where to put the data and the $Ping variable to store the IP address
$DataArray = #()
$Resolution = New-Object System.Object.NetworkInformation.Ping
#Define A Function that will test which computers are on-line and create files to use in the other function.
ForEach ($computer in $Computers)
{
#Defining the variables
$TESTConnection = $null
$Object = $null
$Value = $null
$IPAddress = $null
$NameResolution = $null
$PingAddress = $null
$NIC = $null
$NIC2 = $null
$NICIP = $null
# Ping computer
$TESTConnection = Test-NetConnection -ComputerName $computer | Select-Object PingSucceeded
#If connection is live Get IP address
IF ($TESTConnection.PingSucceeded -eq "True") {
$IPAddress = Test-NetConnection -ComputerName $computer -InformationLevel "Detailed" | Select-Object RemoteAddress
$NIC = Get-WmiObject win32_NetworkAdapterConfiguration -ComputerName $computer | Where-Object {$_.DNSDomain -eq "contoso.com"} | Select-Object MACAddress, DNSHostName
$NIC2 = {Get-WmiObject win32_NetworkAdapterConfiguration -ComputerName $computer | Where-Object {$_.DNSDomain -eq "contoso.com"} | Select-Object IPAddress}
$Value = $TESTConnection.PingSucceeded
$PingAddress = $IPAddress.RemoteAddress
}
Else {
$Value = $TESTConnection.PingSucceeded
$PingAddress = $IPAddress.RemoteAddress
$NICIP = "NOT REACHABLE"
}
# Create object
$Object = New-Object PSObject -Property ([ordered]#{
Computer_Target = $computer
ONLINE = $Value
IPAddress_PING = $PingAddress
IPResolved = $NIC2.IPAddress
NameResolved = $NIC.DNSHostName
MACAddress = $NIC.MACAddress
})
# Add object to array
$DataArray += $Object
#Display object
}
If($DataArray)
{
#Output Array Data into a CSV File
$DataArray | Export-Csv -Path $OutputFile -NoTypeInformation
}
#end of file
OK - I finally figured it out because I kept going around in circles and getting nowhere.
Thank you to Zett42 who led me in the right direction but I was still failing.
My first problem was that I hadn't identified $NIC2 as an Array which was part of my issue when trying $NIC2[0]. However, even after being Defined as an Array it still didn't work.
I then created another array and did this $NICIP = $NIC2.IPAddress and then output $NICIP[0] which works. Not exactly sure why that works.
Here is the entire code
#Definitions of variables
#************************
#Defines the location and name of the text file from where computers will be pulled
$Computers = Get-Content "c:\Temp\computers.txt"
#Defines the variable and format for Date
$Date = Get-date -UFormat %h_%d_%y_%H%M
#Define the name of the files to output
$OutputFile = "C:\Temp\Verify-Computer-$Date.csv" #All Computers On-Line Status will be provided in this file
#Defines the Array where to put the data and the $Ping variable to store the IP address
$DataArray = #()
$Resolution = New-Object System.Object.NetworkInformation.Ping
#Define A Function that will test which computers are on-line and create files to use in the other function.
ForEach ($computer in $Computers)
{
#Defining the variables
$TESTConnection = $null
$Object = $null
$Value = $null
$IPAddress = $null
$NameResolution = $null
$PingAddress = $null
$NIC = $null
$NIC2 = #()
$NICIP = #()
# Ping computer
$TESTConnection = Test-NetConnection -ComputerName $computer | Select-Object PingSucceeded
#If connection is live Get IP address
IF ($TESTConnection.PingSucceeded -eq "True") {
$IPAddress = Test-NetConnection -ComputerName $computer -InformationLevel "Detailed" | Select-Object RemoteAddress
$NIC = Get-WmiObject win32_NetworkAdapterConfiguration -ComputerName $computer | Where-Object {$_.DNSDomain -eq "contoso.com"} | Select-Object MACAddress, DNSHostName
$NIC2 = Get-WmiObject win32_NetworkAdapterConfiguration -ComputerName $computer | Where-Object {$_.DNSDomain -eq "contoso.com"} | Select-Object IPAddress
$Value = $TESTConnection.PingSucceeded
$PingAddress = $IPAddress.RemoteAddress
$NICIP = $NIC2.IPAddress
}
Else {
$Value = $TESTConnection.PingSucceeded
$PingAddress = "NOT REACHABLE"
}
# Create object
$Object = New-Object PSObject -Property ([ordered]#{
Computer_Target = $computer
ONLINE = $Value
IPAddress_PING = $PingAddress
IPResolved1 = $NICIP[0]
NameResolved = $NIC.DNSHostName
MACAddress = $NIC.MACAddress
})
# Add object to array
$DataArray += $Object
#Display object
}
If($DataArray)
{
#Output Array Data into a CSV File
$DataArray | Export-Csv -Path $OutputFile -NoTypeInformation
}
#end of file
Related
I have powershell script to pull down hotfixID, installedon, lastbootuptime and freespace in C drive. (I googled around and changed couple of things I need.) when the Pc is not reachable it will
Write-Warning "$_ cannot be reached, skipping."
I also want to capture the computer name of the failed PC to my CSV. I tried
| Export-Csv C:\test\computerDetails.csv -NoTypeInformation
or append but seems like its not working. can someone please help? below is my whole script.
(Get-Content C:\test\serverlist.txt).Trim() | ForEach {
If (Test-Connection -ComputerName $_ -Count 1 -Quiet)
{
$update = Get-CimInstance Win32_QuickFixEngineering -ComputerName $_ | Sort-Object InstalledOn -Descending | Select-Object -First 1
$os = Get-CimInstance win32_operatingsystem -ComputerName $_
$disk = Get-WmiObject Win32_LogicalDisk -ComputerName $_ -Filter "DeviceID='C:'"
$props = #{
ComputerName = $_
HotFixID = $update.HotFixID
InstalledOn = $update.InstalledOn
lastbootuptime = $os.LastBootUpTime
FreeSpace_GB = $disk.FreeSpace / 1GB
}
New-Object PsObject -Property $props
}
Else {
Write-Warning "$_ cannot be reached, skipping." | Export-Csv C:\test\computerDetails.csv -NoTypeInformation
}
} | Sort ComputerName |
Select ComputerName,HotFixID,InstalledOn,lastbootuptime,FreeSpace_GB |
Export-Csv C:\test\computerDetails.csv -NoTypeInformation
Main problem with adding it to the CSV is that it is a string. If you treat the erroneous machines the same as successful ones, then you can throw them in the same CSV.
I have added an ArrayList there as the storage variable and then for each computer it creates a temp PSObject to store your results in, overwriting the variable each loop but not before dumping the variable into the ArrayList for export at the end.
$Errors = New-Object System.Collections.ArrayList
(Get-Content C:\test\serverlist.txt).Trim() | ForEach {
$Temp = New-Object -TypeName PSObject
If (Test-Connection -ComputerName $_ -Count 1 -Quiet) {
$update = Get-CimInstance Win32_QuickFixEngineering -ComputerName $_ | Sort-Object InstalledOn -Descending | Select-Object -First 1
$os = Get-CimInstance win32_operatingsystem -ComputerName $_
$disk = Get-WmiObject Win32_LogicalDisk -ComputerName $_ -Filter "DeviceID='C:'"
$props = [ordered]#{
ComputerName = $_
HotFixID = $update.HotFixID
InstalledOn = $update.InstalledOn
lastbootuptime = $os.LastBootUpTime
FreeSpace_GB = $disk.FreeSpace / 1GB
Error = "Success"
}
$Temp | Add-Member -NotePropertyMembers $props -TypeName temp
} Else {
$props = [ordered]#{
ComputerName = $_
Error = "Cannot be reached"
}
$Temp | Add-Member -NotePropertyMembers $props -TypeName temp
Write-Warning "$_ cannot be reached, skipping."
}
$Errors.Add($Temp) > $null
}
$Errors | Export-Csv C:\temp\computerDetails.csv -NoTypeInformation -Append
I'm trying to get the hostname and the MAC address from all PCs in the Active Directory. I know that MAC addresses are not in the Activce Directory. That's why I already used a small script from someone else. The point is that I have to make a list of hostnames, which I can do, but then the other script runs into a problem because some computers are not online.
Can anyone help me get a list with only the pc's that are online?
This is the part that searches the list I create with hostnames.
$Computers = Import-CSV C:\Users\admin_stagiair\Desktop\Computers.txt
$result = #()
foreach ($c in $Computers){
$nic = Invoke-Command {
Get-WmiObject Win32_NetworkAdapterConfiguration -Filter 'ipenabled = "true"'
} -ComputerName $c.Name
$x = New-Object System.Object | select ComputerName, MAC
$x.Computername = $c.Name
$x.Mac = $Nic.MACAddress
$result += $x
}
$result | Export-Csv C:\Users\admin_stagiair\Desktop\Computers.csv -Delimiter ";" -NoTypeInformation
And this is the part that I tried to make search the list and filter out the online computers, which absolutely does not work and I can't figure out how to do it.
$Computers = Import-Csv C:\Users\admin_stagiair\Desktop\Computers.txt
foreach ($c in $Computers) {
$ping = Test-Connection -Quiet -Count 1
if ($ping) {
$c >> (Import-Csv -Delimiter "C:\Users\admin_stagiair\Desktop\online.txt")
} else {
"Offline"
}
}
Last bit, this is the part I use to create a list of all computers in the Active Directory.
Get-ADComputer -Filter {enabled -eq $true} -Properties * |
select Name > C:\Users\(user)\Desktop\Computers.txt
If you only want one property from Get-ADComputer don't fetch all
a computer could have more than one MAC, to avoid an array be returned join them.
$result += inefficiently rebuilds the array each time, use a PSCustomObject instead.
Try this (untested):
EDIT: first test connection, get MAC only when online
## Q:\Test\2018\09\18\SO_52381514.ps1
$Computers = (Get-ADComputer -Filter {enabled -eq $true} -Property Name).Name
$result = ForEach ($Computer in $Computers){
If (Test-Connection -Quiet -Count 1 -Computer $Computer){
[PSCustomPbject]#{
ComputerName = $Computer
MAC = (Invoke-Command {
(Get-WmiObject Win32_NetworkAdapterConfiguration -Filter 'ipenabled = "true"').MACAddress -Join ', '
} -ComputerName $Computer)
Online = $True
DateTime = [DateTime]::Now
}
} Else {
[PSCustomPbject]#{
ComputerName = $Computer
MAC = ''
Online = $False
DateTime = [DateTime]::Now
}
}
}
$result | Export-Csv C:\Users\admin_stagiair\Desktop\Computers.csv -Delimiter ";" -NoTypeInformation
What about trying something like this:
# Get all computers in list that are online
$Computers = Import-Csv C:\Users\admin_stagiair\Desktop\Computers.txt |
Select-Object -ExpandProperty Name |
Where-Object {Test-Connection -ComputerName $_ -Count 1 -Quiet}
# Grab the ComputerName and MACAddress
$result = Get-WmiObject -ComputerName $computers -Class Win32_NetworkAdapterConfiguration -Filter 'ipenabled = "true"' |
Select-Object -Property PSComputerName, MacAddress
$result | Export-Csv C:\Users\admin_stagiair\Desktop\Computers.csv -Delimiter ";" -NoTypeInformation
I have a script where the script check NIC binding order.
$result = Invoke-Command -ComputerName $ComputerName -ScriptBlock {
function Get-BindOrder {
$Binding = (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Linkage").Bind
$Return = New-Object PSobject
$BindingOrder = #()
foreach ($Bind in $Binding) {
$DeviceId = $Bind.Split("\")[2]
$Adapter = (Get-WmiObject Win32_Networkadapter | Where {$_.GUID -eq $DeviceId }).NetConnectionId
$BindingOrder += $Adapter
}
$BindingOrder
} #EndFunction
CLS
Get-BindOrder
}
$adapteresult = $result -join (",")
when I echo this $adapteresult = $result, I am getting an output as below
PS C:\> $adapteresult
vEthernet (10.211.14.0_20)
storage
Ethernet 5
Ethernet 4
Ethernet 2
Ethernet 6
The same variable when I added $adapteresult = $result -join (","), I am getting out put with extra command in between.
vEthernet (10.241.24.0_21),storage,Ethernet 5,,Ethernet 4,Ethernet 2,Ethernet 6,,
I do not want any extra comma in output. Expecting output like below:
vEthernet (10.241.24.0_21),storage,Ethernet 5,Ethernet 4,Ethernet 2,Ethernet 6,
(Get-WmiObject Win32_Networkadapter | Where {$_.GUID -eq $guid}).NetConnectionId appears to be returning $nulls that you are capturing in $BindingOrder. Displayed on the screen they take up no space but they are there regardless. Running a condensed version of your code locally on my machine ...
$results = ((Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Linkage").Bind | Foreach-Object {
$guid = $_.split("\")[2]
(Get-WmiObject Win32_Networkadapter | Where {$_.GUID -eq $guid}).NetConnectionId
})
Using that lets look at $results:
$results
$results.Count
$results -contains $null
Local Area Connection
6
True
On my machine I only have one match for Local Area Connection. However the $results have stored 6 items at least one of which is a $null. You need to filter out these results it seems in your query.
$Adapter = (Get-WmiObject Win32_Networkadapter | Where {$_.GUID -eq $DeviceId }).NetConnectionId
if($Adapter){
$BindingOrder += $Adapter
}
Should do it. if $adapter is null or empty string then it won't be added to $bindingorder
I have corrected myself and its working fine now
$a = (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Linkage").Bind | ForEach-Object {
$guid = ($_ -split '\\')[2]
Get-WmiObject -Query "SELECT * FROM Win32_NetworkAdapter WHERE GUID='$guid'" |
Select-Object -ExpandProperty NetConnectionID
}
$b = $a -join","
I'm trying to pull default gateway info from get-wmiobject and then add a new property to an object, using switch to fill in the info, by using a Foreach-object within a foreach. I'm knocking my head on the wall. Can you tell me what I'm doing wrong?
I can export the CN and the DefaultIpgateway fine, but the location property and the $location variable isn't updating. I want to get that $location variable to switch and then put the data into Location Property of $output.
I'm very new to Powershell. Any help is appreciated.
$hostname = Get-Content -path C:\workspace\location_project\Workspace_818\pcs.txt
$output = foreach ($h in $hostname) {
#get-wmiobject will get the comptuer info for each system.
get-wmiobject Win32_NetworkAdapterConfiguration -filter "IPEnabled=TRUE" -computername $H | foreach-object {
new-object PSObject -property #{
"CN" = $h
"DefaultIPGateway" = $_.DefaultIPGateway[0]
"Location" = $location ## = $output."DefaultIPGateway"[0])
}
switch ($output."DefaultIPGateway") {
10.10.1.2{$location = "Location1"}
10.10.1.20{$location = "Locaiton2"}
}
select-object DefaultIPGateway, CN, Location
}
}
$output | Export-csv C:\workspace\CN_Gateway_Loc_Get2.csv -NoTypeInformation -force
In your sample code, you add $location the object, when its still empty. You have to set $location before you add it to the object.
Further considerations:
use Get-CimInstance over Get-WmiObject because it uses WinRM to connect to remote computers by default and acceppt CimSession as input, which is has the lowest footprint. And maybe most important: It queries the computers in parallel which makes the complete script in scale suuuuper fast!
Get-CimInstance (as well as Get-WmiObject) accepts multiple CimSessions/ComputerNames as input, hence you don't need the foreach.
[PSCustomObject] is faster then New-Object
After applying the improvements, your code would look somewhat like this:
$hostname = Get-Content -path C:\workspace\location_project\Workspace_818\pcs.txt
$csvPath = 'C:\workspace\CN_Gateway_Loc_Get2.csv'
$CimSessions = New-CimSession -ComputerName $hostname
$NetworkAdapterConfiguration = Get-CimInstance -CimSession $CimSessions -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled=TRUE"
$output = foreach ($adapter in $NetworkAdapterConfiguration) {
switch ($adapter.DefaultIPGateway[0]) {
'10.10.1.2' {$location = 'Location1'}
'10.10.1.20' {$location = 'Location2'}
default {$location = 'Unknown'}
}
[PSCustomObject]#{
ComputerName = $adapter.PSComputername
DefaultIPGateway = $adapter.DefaultIPGateway[0]
Location = $location
}
}
$output | Export-Csv -Path $csvPath -NoTypeInformation -Force
If that works for you, I highly recommend to convert your script into a tool (Cmdlet). Make a function that allows ComputerName and CimSession as input. Output would be your already defined object. This would allow anyone to use the tool as he needs to use it (instead of, how you want them to use it). If your new Cmdlet is called Get-DefaultGateway you can do things like this:
Get-DefaultGateway -ComputerName (Get-ADComputer).Name | Out-GridView
or
$CimSessions = Get-Clipboard | New-CimSession
Get-DefaultGateway -CimSession $CimSessions | Export-Csv -Path $env:TEMP\export.csv
I would do something like this. You need to get the location first. So I set $location to the result of switch. Then I create the pscustomobject with the parameters you wanted. You don't need a Select-Object because the three attributes are the only attributes in the object.
$hostname = Get-Content -Path C:\workspace\location_project\Workspace_818\pcs.txt
$output = foreach ($h in $hostname) {
#get-wmiobject will get the comptuer info for each system.
Get-WmiObject Win32_NetworkAdapterConfiguration -Filter "IPEnabled=TRUE" -ComputerName $H `
| ForEach-Object -Process {
$location = switch ($_.DefaultIPGateway) {
10.10.1.2 { 'Location1'; break }
10.10.1.20 { 'Locaiton2'; break }
default { 'unknown' }
}
[pscustomobject] #{
"CN" = $h
"DefaultIPGateway" = $_.DefaultIPGateway[0]
"Location" = $location
}
}
}
$output | Export-csv C:\workspace\CN_Gateway_Loc_Get2.csv -NoTypeInformation -force
I have a script that scans all my servers in my domains and outputs to two separate CSV files - one simple and one extensive.
I write one line at the time to my csv.. This results in thousands of file-open and file-close.. I've lurked around and understand that I should first gather all the info and write it all in one sweep at the end of the script. But how do I do this with export-csv (preferably using a function)?
And is there a way I can use the same function for short and long list?
The script performs numerous tasks on each domain/server, but I've trimmed it down to this for your viewing pleasure:
$domains = "no","se","dk"
# Loop through specified forests
foreach ($domain in $domains) {
# List all servers
$servers = Get-QADComputer
# Looping through the servers
foreach ($server in $servers) {
# GENERATE LONGLIST #
# Ping server
if (Test-Connection -ComputerName $server.name -count 1 -Quiet )
{
$Reachable = "Yes"
# Fetch IP address
$ipaddress = [System.Net.Dns]::GetHostAddresses($Server.name)|select-object IPAddressToString -expandproperty IPAddressToString
# Formatting output and export all info to CSV
New-Object -TypeName PSObject -Property #{
SystemName = ($server.name).ToLower()
Reachable = $Reachable
Domain = $server.domain
IPAddress = $IPAddress
} | Select-Object SystemName,Domain,IPAddress| Export-Csv -Path "longexport.csv" -append
}
else # Can't reach server
{
$reachable = "No"
$IPAddress = "Unknown"
# Formatting output and export all info to CSV
New-Object -TypeName PSObject -Property #{
SystemName = ($server.name).ToLower()
Reachable = $Reachable
Domain = $server.domain
} | Select-Object SystemName,Domain,IPAddress| Export-Csv -Path "shortexport.csv" -append
}
}
}
(and let me just say that I know that I cannot do -append with export-csv, but I am using a function that let's me do this..)
You are exporting the same amount of properties to each file so I'm not sure I understand why one of them is considered long and one short. Anyway, I suggest the following, don't assign all computers to a variable, it can take up a lot of RAM, instead use a streaming way (one object at a time) and use foreach-object. Also, since I find no difference in the files I output to the file at the end of each domain operation (one per domain). And with another twick you could write only once to the file.
$domains = "no","se","dk"
foreach ($domain in $domains) {
Get-QADComputer -Service $domain -SizeLimit 0 | Foreach-Object {
$reachable = Test-Connection -ComputerName $_.Name -count 1 -Quiet
if($reachable)
{
$IPAddress = [System.Net.Dns]::GetHostAddresses($_.Name)|select-object IPAddressToString -expandproperty IPAddressToString
}
else
{
$IPAddress = $null
}
New-Object -TypeName PSObject -Property #{
SystemName = $_.Name.ToLower()
Reachable = $reachable
Domain = $_.Domain
IPAddress = $IPAddress
} | Select-Object SystemName,Domain,IPAddress
} | Export-Csv -Path export.csv -Append
}
You'll need to keep data in memory to prevent multiple file open/closes.
You can do this by adding your data to an array like this:
$myData = #()
$myData += New-Object -TypeName PSObject -Property #{
SystemName = ($server.name).ToLower()
Reachable = $Reachable
Domain = $server.domain
} | Select-Object SystemName,Domain,IPAddress
Then at the end of processing convert the array to CSV using $myData | ConvertTo-CSV | Out-File C:\Data.csv or just $myData | Export-Csv C:\Data.csv.