I was wondering how I could return the Resolve-DnsName output from my Test-Connection script and add it to the CSV I created.
I like to capture the Name, Type, TTL, Section from that please.
Only invoke the Resolve-DnsName when the ping is not successful.
$servers = Get-Content "servers.txt"
$collection = $()
foreach ($server in $servers)
{
$status = #{ "ServerName" = $server; "TimeStamp" = (Get-Date -f s) }
$result = Test-Connection $server -Count 1 -ErrorAction SilentlyContinue
if ($result)
{
$status.Results = "Up"
$status.IP = ($result.IPV4Address).IPAddressToString
}
else
{
$status.Results = "Down"
$status.IP = "N/A"
$status.DNS = if (-not(Resolve-DnsName -Name $server -ErrorAction SilentlyContinue))
{
Write-Output -Verbose "$server -- Not Resolving"
}
else
{
"$server resolving"
}
}
New-Object -TypeName PSObject -Property $status -OutVariable serverStatus
$collection += $serverStatus
}
$collection | Export-Csv -LiteralPath .\ServerStatus3.csv -NoTypeInformation
but nothing new is added to the CSV.
You ran into a PowerShell gotcha. PowerShell determines the columns displayed in tabular/CSV output from the first object processed. If that object doesn't have a property DNS that column won't be shown in the output, even if other objects in the list do have it. If other objects don't have properties that were present in the first object they will be displayed as empty values.
Demonstration:
PS C:\> $a = (New-Object -Type PSObject -Property #{'a'=1; 'b'=2}),
>> (New-Object -Type PSObject -Property #{'a'=3; 'b'=4; 'c'=5}),
>> (New-Object -Type PSObject -Property #{'b'=6; 'c'=7})
>>
PS C:\> $a | Format-Table -AutoSize
a b
- -
1 2
3 4
6
PS C:\> $a[1..2] | Format-Table -AutoSize
c b a
- - -
5 4 3
7 6
If you want to generate tabular output always create your objects uniformly with the same set of properties. Choosing sensible defaults even allows you to reduce your total codebase.
$collection = foreach ($server in $servers) {
$status = New-Object -Type PSObject -Property #{
'ServerName' = $server
'TimeStamp' = Get-Date -f s
'Results' = 'Down'
'IP' = 'N/A'
'HasDNS' = [bool](Resolve-DnsName -Name $server -EA SilentlyContinue)
}
$result = Test-Connection $server -Count 1 -EA SilentlyContinue
if ($result) {
$status.Results = 'Up'
$status.IP = ($result.IPV4Address).IPAddressToString
}
$status
}
Related
I'd like the following code to add the specified columns if it finds the appropriate graphics adapter in a pc.
Right now, my if/elseif statements are throwing all kinds of errors and I'm thinking its because I put it in the wrong section of the code. The columns are not being generated as how I would like for it to.
Any advice?
# User needs to create a txt file containing hostnames.
function ReadHostnames($initialDirectory) {
[void] [System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms')
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
if ($initialDirectory) { $OpenFileDialog.initialDirectory = $initialDirectory }
$OpenFileDialog.filter = 'All files (*.*)|*.*'
[void] $OpenFileDialog.ShowDialog()
return $OpenFileDialog.FileName
}
($FilePermissions = ReadHostnames C:\)
$FilePermissions = Get-Content $FilePermissions
write-host "Please wait while gathering information..."
$counter = 0
foreach ($computernames in $FilePermissions)
{
Write-host "Processing $computernames ($counter/$($FilePermissions.count))"
IF (Test-Connection -BufferSize 32 -Count 1 -ComputerName $computernames -Quiet)
{
$Computersystem = Get-WMIObject Win32_ComputerSystem -ComputerName $computernames -AsJob
$videocontroller = Get-WmiObject win32_videocontroller -ComputerName $computernames -AsJob
$bioscontroller1 = Get-WmiObject win32_bios -ComputerName $computernames -AsJob
$bioscontroller2 = Get-WmiObject -Class:Win32_ComputerSystem -ComputerName $computernames -AsJob
$userlogon = Get-CimInstance -ClassName Win32_ComputerSystem -Property UserName -ComputerName $computernames
Wait-Job -Job $Computersystem,$videocontroller,$bioscontroller -Timeout 10 | out-Null
$computersystem_output = Receive-Job $Computersystem
$intelvideocontroller_output = Receive-Job $videocontroller | ? {$_.name -ilike "*Intel*"}
$nvidiavideocontroller_output = Receive-Job $videocontroller | ? {$_.name -ilike "*NVIDIA*"}
$AMDvideocontroller_output = Receive-Job $videocontroller | ? {$_.name -ilike "*AMD*"}
$bioscontroller1_output = Receive-Job $bioscontroller1
$bioscontroller2_output = Receive-Job $bioscontroller2
# Creating spreadsheet headers
$newrow = [Pscustomobject] #{
Host_name = $computersystem_output.name
Model_Name = $bioscontroller2_output.Model
Serial_Number = $bioscontroller1_output.SerialNumber
BIOS_Version = $bioscontroller1_output.SMBIOSBIOSVersion
Last_User_Logon = $userlogon.UserName
If ($intelvideocontroller_output -ilike "*Intel*")
{ Intel_Card = $intelvideocontroller_output.name
IntelGraphics_Version = $intelvideocontroller_output.DriverVersion}
ElseIf ($nvidiavideocontroller_output -ilike "*NVIDIA*")
{ Nvidia_Card = $nvidiavideocontroller_output.name
NvidiaGraphics_Version = $nvidiavideocontroller_output.DriverVersion }
ElseIf ( $AMDvideocontroller_output -ilike "*AMD*")
{ AMD_Card = $AMDvideocontroller_output.name
AMDGraphics_Version = $AMDvideocontroller_output.DriverVersion }
}
$newrow | export-csv -path c:\HostnameData.csv -Append -NoTypeInformation
Remove-Job -job $Computersystem,$videocontroller,$bioscontroller1,$bioscontroller2 -Force
$counter++
}
Else
{
write-Host "The remote computer "$computernames" is Offline"
}
}
The logic would have to be wrapped inside some type of operator such as the grouping, or sub-expression operator to be allowed as the assignment of the name. A more concise solution is using a switch statement before your pscustomobject construct and having a dynamic assignment.
$card_type,
$card_version =
switch -Wildcard ($videocontroller.Name)
{
'*Intel*' { 'Intel_Card', 'IntelGraphics_Version' }
'*NVIDIA*' { 'Nvidia_Card', 'NvidiaGraphics_Version' }
'*AMD*' { 'AMD_Card', 'AMDGraphics_Version' }
$_ { 'N/A_Card', 'N/A_Version' }
}
$newrow = [pscustomobject]#{
Host_name = $computersystem_output.name
Model_Name = $bioscontroller2_output.Model
Serial_Number = $bioscontroller1_output.SerialNumber
BIOS_Version = $bioscontroller1_output.SMBIOSBIOSVersion
Last_User_Logon = $userlogon.UserName
$card_type = $videocontroller.Name
$card_version = $videocontroller.DriverVersion
}
Now the type of card gets saved to $card_type, and the driver version name gets saved to $card_version; it also accounts for cards that didn't meet that criteria and defaults to 'N/A'.
On another note, I personally don't see why you're querying the same class of Win32_ComputerSystem more than once while using jobs and having them wait. You are also using 2 different type of cmdlets that do the same thing when querying those classes. You should only need the first variable assignments and you'd only have to reference them once.
EDIT:
You also can't append properties with different names to a csv that has a column already being used by a different name.
What I am trying to achieve here is add the servers and the updates that are not installed on the server to an array and create a new object that is going to display the names of the servers in one column and the missing updates on another column, but at the end I am getting an empty Grid-View table.
The values for the servers and updates are read from a file.
Write-Host
#Read the password from stdin and store it in a variable
$password = Read-Host -AsSecureString -Prompt "Enter your password"
Write-Host
#Get credentials and password for later user
$cred = New-Object System.Management.Automation.PSCredential ("Administrator#testing.local", $password )
#Get the list of available servers to test
$servers = Get-Content -Path $HOME\Desktop\servers.txt
#Get the list of available updates that need to be installed on the server
$available_updates = Get-Content $HOME\Desktop\update.txt
$add_updates = #()
$add_updates_and_servers = #()
#Get each server name from the list and execute the following commands
foreach ($server in $servers) {
#Test if the server is reponding
$ping = Test-Connection $server -Count 1 -Quiet
#If the above command returns True continue
if ($ping -eq "True") {
#Write a message saying Testing server_name
Write-Host "Testing $server"
foreach ($update in $available_updates) {
#Check if update is installed
$updates_from_os = Invoke-Command -ComputerName $server -Credential $cred -ScriptBlock { Get-HotFix | Select-Object -Property HotFixID | Where-Object -Property HotFixID -EQ $Using:update } -HideComputerName | Select-Object -ExpandProperty HotFixID
if (!$updates_from_os) {
$add_updates += $update
}
}
New-Object -TypeName PSObject -Property $updates -OutVariable final
$updates = #{
"Server" = $server
"Updates" = $add_updates
}
}
$add_updates_and_servers += $final
}
$add_updates_and_servers | Out-GridView
For what is probably happening with your script:
I suspect that each time you calling the statement New-Object -TypeName PSObject -Property $updates -OutVariable final You overwriting any previous created $final object which references to the same objects as your $add_updates_and_servers collection.
Anyways, try to avoid using the increase assignment operator (+=) to create a collection, instead stream the results to a variable (or even better, directly to next/final cmdlet: ... }| Out-GridView).
Something like:
$add_updates_and_servers = foreach ($server in $servers) {
$ping = Test-Connection $server -Count 1 -Quiet
if ($ping -eq "True") {
Write-Host "Testing $server"
$add_updates = #(
foreach ($update in $available_updates) {
$updates_from_os = Invoke-Command -ComputerName $server -Credential $cred -ScriptBlock { Get-HotFix | Select-Object -Property HotFixID | Where-Object -Property HotFixID -EQ $Using:update } -HideComputerName | Select-Object -ExpandProperty HotFixID
if (!$updates_from_os) { $update }
}
)
[PSCustomObject]#{
"Server" = $server
"Updates" = $add_updates
}
}
}
Note: in case you want each $update in a separate column, also have a look at: Not all properties displayed
I create variables in a ForEach loop using data collected from a CSV file like this:
New-Variable -Name $FlexVPN.'IP-adress' -Value (New-Object PSObject -Property #{
IP = $FlexVPN.'IP-adress'
Information = $FlexVPN.'Information'
Priority = $FlexVPN.'Priority'
RegisteredUp = $RegisteredUp
RegisteredDown = $RegisteredDown
ResponseTime = $Result = try{ Test-Connection -ComputerName $FlexVPN.'IP-adress' -Count $FlexVPN.'Priority' -ErrorAction Stop | Select ResponseTime} catch [System.Net.NetworkInformation.PingException] { $_.exception | PingFailed }})
What I'm then trying to do is to change the values of RegisteredUp and RegisteredDown depending of the respond of the ping.
I doesn't understand the New-Member stuff which I have tried but faild using.
Now I tried Set-Variable but I don´t get how to only change a Object within the Variable?
Set-Variable -Name $FlexVPN.'IP-adress' -Value (New-Object PSObject -Property #{RegisteredDown = "TESTING"})
I don´t get any errors neither it´s working.
To explain further.
If no respond on ping set Get-Date in RegisteredDown for that Variable.
If respond on ping ser Get-Date in RegisteredUp for that Variable.
I then use if/else to use the result somehow in the next version ;)
Edit
# Clear variables after loop
Remove-Variable * -force -erroraction silentlycontinue
function PingFailed {
# Add date and time when IP-address first didn't responded
$FlexVPN.RegisteredDown = 'AnotherTest'
# If only error should be printed
if($PrintError -eq 'Yes'){Write-Host -ForegroundColor Red $FlexVPN.'IP-adress' "," $FlexVPN.'Information'}
##########################################################################
####################### NO CHANGES ABOVE THIS LINE #######################
##########################################################################
# Choose between printing output or not for all rows in CSV-file [Yes/No]
$PrintOutput = 'Yes'
# Choose between printing out error or not [Yes/No]
$PrintError = 'No'
##########################################################################
####################### NO CHANGES BELOW THIS LINE #######################
##########################################################################
# Import CSV-file to Powershell to use data in code
$FlexVPNlist = Import-Csv -Path $PSScriptRoot\PingIPEmail.csv -Header 'IP-adress', 'Information', 'Priority' -Delimiter ';' -Encoding UTF7
Foreach($FlexVPN in $FlexVPNlist) {
New-Variable -Name $FlexVPN.'IP-adress' -Value (New-Object PSObject -Property #{
IP = $FlexVPN.'IP-adress'
Information = $FlexVPN.'Information'
Priority = $FlexVPN.'Priority'
RegisteredDown = 'Test'
ResponseTime = $Result = try{ Test-Connection -ComputerName $FlexVPN.'IP-adress' -Count $FlexVPN.'Priority' -ErrorAction Stop | Select ResponseTime} catch [System.Net.NetworkInformation.PingException] { $_.exception | PingFailed }})
if($PrintOutput -eq 'Yes'){
if ($host.name -eq 'Windows PowerShell ISE Host') {if ($Result.ResponseTime -eq $null) { $Host.UI.RawUI.BackgroundColor = ($bckgrnd = 'Red') } else { $psISE.Options.RestoreDefaults() }}
[PSCustomObject]#{
"IP address" = $FlexVPN.'IP-adress'
"Information" = $FlexVPN.'Information'
"Priority" = $FlexVPN.'Priority'
"Response time" = $Result.ResponseTime
"RegisteredDown" = 'Test'
}}
}
}
My Second try above works fine until I catch an exeption during ping and goes to my function PingFailed.
I want to run that function when an IP-address doesn´t respond and add Get-Date to RegisteredDown in those cases.
The error I recieve is:
At C:\Temp\Powershell scripts\PingIPEmail\PingIPEmail.ps1:49 char:13
+ $FlexVPN.RegisteredDown = 'AnotherTest'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting`
Tried the other code
# Importing a csv
$FlexVPNList = Import-Csv -Path 'C:\Temp\Powershell scripts\PingIPEmail\PingIPEmail.csv' -Header 'IP-adress', 'Information', 'Priority' -Delimiter ';' -Encoding UTF7
Foreach($FlexVPN in $FlexVPNlist) {
$FlexVPN.GetType() # Object[]
$FlexVPN[0].GetType() # PSCustomObject
($FlexVPN[0] | gm -MemberType NoteProperty).Count # 3 Noteproperties
$FlexVPN | % {
Add-Member -InputObject $_ -NotePropertyName 'RegisteredUp' -NotePropertyValue 1 -Force
Add-Member -InputObject $_ -NotePropertyName 'RegisteredDown' -NotePropertyValue 1 -Force
}
($FlexVPN[0] | gm -MemberType NoteProperty).Count # 5 Noteproperties
$Result = try{ Test-Connection -ComputerName $FlexVPN.'IP-adress' -Count $FlexVPN.'Priority' -ErrorAction Stop | Select ResponseTime} catch [System.Net.NetworkInformation.PingException] { $_ }
if ($Result.ResponseTime -eq $null){
if ($host.name -eq 'Windows PowerShell ISE Host') { $Host.UI.RawUI.BackgroundColor = ($bckgrnd = 'Red') }
$FlexVPN.RegisteredDown = Get-Date
[PSCustomObject]#{
"IP address" = $FlexVPN.'IP-adress'
"Information" = $FlexVPN.'Information'
"Priority" = $FlexVPN.'Priority'
"Response time" = $Result.ResponseTime
"RegisteredUp" = $FlexVPN.RegisteredUp
"RegisteredDown" = $FlexVPN.RegisteredDown
}
}
if ($Result.ResponseTime -ge '0'){
if ($host.name -eq 'Windows PowerShell ISE Host') { $psISE.Options.RestoreDefaults() }
$FlexVPN.RegisteredUp = Get-Date
[PSCustomObject]#{
"IP address" = $FlexVPN.'IP-adress'
"Information" = $FlexVPN.'Information'
"Priority" = $FlexVPN.'Priority'
"Response time" = $Result.ResponseTime
"RegisteredUp" = $FlexVPN.RegisteredUp
"RegisteredDown" = $FlexVPN.RegisteredDown
}
}
This code if I understand correctly reuse the same variable for each row in my CSV file.
I want to create one variable for each row (name them the IP-address) so that I can reuse the values stored for as long as the script i running.
Looks like you are overcomplicating things. You can create a new variable (object) like this
$FlexVPN = [PSCustomObject] #{
Information='Test'
}
Show the value of Information
$FlexVPN.Information
Change the value of Information
$FlexVPN.Information = 'AnotherTest'
Show the changed value of Information
$FlexVPN.Information
a valid use case for using new-variable would be if you dynamically create/use variables
Edit
your intent is not actual clear to me but following testbed might get you some new ideas to proceed from
# Mimick importing a csv
$FlexVPN = #'
IP-Adress,Information,Priority
1.1.1.1,FlexVPN,1
2.2.2.2,FlexVPN,2
'# | ConvertFrom-Csv
$FlexVPN.GetType() # Object[]
$FlexVPN[0].GetType() # PSCustomObject
($FlexVPN[0] | gm -MemberType NoteProperty).Count # 3 Noteproperties
$FlexVPN | % {
Add-Member -InputObject $_ -NotePropertyName 'RegisteredUp' -NotePropertyValue 1 -Force
Add-Member -InputObject $_ -NotePropertyName 'RegisteredDown' -NotePropertyValue 1 -Force
}
($FlexVPN[0] | gm -MemberType NoteProperty).Count # 5 Noteproperties
Managed what I wanted by doing like this:
# Clear variables after loop
Remove-Variable * -force -erroraction silentlycontinue
# Importing a csv
$FlexVPNList = Import-Csv -Path 'C:\Temp\Powershell scripts\PingIPEmail\PingIPEmail.csv' -Header 'IP', 'Information', 'Priority' -Delimiter ';' -Encoding UTF7
$FlexVPNList | % {
Add-Member -InputObject $_ -NotePropertyName 'RegisteredUp' -NotePropertyValue '' -Force
Add-Member -InputObject $_ -NotePropertyName 'RegisteredDown' -NotePropertyValue '' -Force
Add-Member -InputObject $_ -NotePropertyName 'ResponseTime' -NotePropertyValue '' -Force
}
Foreach($FlexVPN in $FlexVPNlist) {
$Ping = try{ Test-Connection -ComputerName $FlexVPN.IP -Count $FlexVPN.'Priority' -ErrorAction Stop | Select ResponseTime } catch [System.Net.NetworkInformation.PingException] { $_ }
if($Ping.ResponseTime -ge '0'){
$FlexVPN.RegisteredUp = Get-Date
$FlexVPN.ResponseTime = $Ping.ResponseTime
}
if($Ping.ResponseTime -eq $null){ $FlexVPN.RegisteredDown = Get-Date }
New-Variable -Name $FlexVPN.IP -Value (New-Object PSObject -Property #{
IP = $FlexVPN.IP
Information = $FlexVPN.Information
Priority = $FlexVPN.Priority
RegisteredUp = $FlexVPN.RegisteredUp
RegisteredDown = $FlexVPN.RegisteredDown
ResponseTime = $Ping.ResponseTime
})
[PSCustomObject]#{
"IP address" = $FlexVPN.IP
"Information" = $FlexVPN.Information
"Priority" = $FlexVPN.Priority
"Response time" = $FlexVPN.ResponseTime
"RegisteredUp" = $FlexVPN.RegisteredUp
"RegisteredDown" = $FlexVPN.RegisteredDown
}
}
I can now do stuff If computer responded or not!
I am new to PS scripting. I am in need of help with my code.
purpose: ping a list of IP's from a .txt file and output the result in csv format. Here is my code thus far.
$Iplist = Get-Content ips.txt
$group = $()
foreach ($ip in $Iplist)
{
$status = #{ "ServerIP Name" = $ip; "TimeStamp" = (Get-Date -f s) }
if (Test-Connection $ip -Count 4 -ea 0 | measure-Object -Property ResponseTime -Average)
{
$status["Results"] = "Up"
}
else
{
$status["Results"] = "Down"
}
New-Object -TypeName PSObject -Property $status -OutVariable serverStatus
$group += $serverStatus
}
$group | export-csv c:\ping\results.csv -NoTypeInformation
Test-Connection returns a Win32_PingStatus object.
To see what else is available on this object in PowerShell type:
$ping = Test-Connection www.google.com #or insert favorite url here
$ping | Format-List | Out-String
Test-Connection doesn't just return a bool. You're really close but you have to assign the return value in order to calculate the average on success:
$Iplist = Get-Content ips.txt
$group = #()
foreach ($ip in $Iplist) {
$status = #{ "ServerIP Name" = $ip; "TimeStamp" = (Get-Date -f s) }
$pings = Test-Connection $ip -Count 4 -ea 0
if ($pings) {
$status["AverageResponseTime"] =
($pings | Measure-Object -Property ResponseTime -Average).Average
$status["Results"] = "Up"
}
else {
$status["Results"] = "Down"
}
New-Object -TypeName PSObject -Property $status -OutVariable serverStatus
$group += $serverStatus
}
$group | Export-Csv c:\ping\results.csv -NoTypeInformation
Two issues,
I want a constaint ping to the two servers, and output to a .csv file.
The script below only runs twice and the output doesn't work. I'm a power newbie so please go easy.
$servers = "server1","server2"
$collection = $()
foreach ($server in $servers)
{
$status = #{ "ServerName" = $server; "TimeStamp" = (Get-Date -f s) }
if (Test-Connection $server -Count 1 -ea 0 -Quiet)
{
$status["Results"] = "Up"
}
else
{
$status["Results"] = "Down"
}
New-Object -TypeName PSObject -Property $status -OutVariable serverStatus
$collection += $serverStatus
}
$collection | Export-Csv -LiteralPath .\ServerStatus.csv -NoTypeInformation
The output works fine. Just run the script and then Invoke-Item ServerStatus.csv
If you want it to run forever just wrap the whole thing in a while loop:
$servers = "server1","server2"
$collection = $()
while(1) {
foreach ($server in $servers)
{
...
}
$collection | Export-Csv -LiteralPath .\ServerStatus.csv -NoTypeInformation
}