Output if/else statement results to formatted table - powershell

I have written an environment IP check script in Powershell which works but I can't figure out how to display the output to screen in a formatted table which auto sizes the columns.
$infile = Import-Csv "~\Env_IPlist.csv" -Delimiter ","
$arr1 = #()
$IP_Addresses = $infile |
Select-Object "Device_Type", "Device_Name", "IP_Address",
"IP_Role", "Location"
Write-Host "Started Ping Test ..."
foreach ($IP in $IP_Addresses) {
if (Test-Connection $IP.("IP_Address") -Quiet -Count 1 -ErrorAction SilentlyContinue) {
Write-Host $IP.("Device_Name") ":" $IP.("IP_Address") "Ping successful" -ForegroundColor Green
} else {
Write-Host $IP."Device_Name" ":" $IP.("IP_Address") "Ping failed" -ForegroundColor Red -BackgroundColor white
}
}
Write-Host "Ping Test Completed!"

Add the Test-Connection result via a calculated property, then pipe the output to Format-Table.
$infile |
Select-Object Device_Type, Device_Name, IP_Address, IP_Role, Location,
#{n='Online';e={
[bool](Test-Connection $_.IP_Address -Quiet -Count 1 -EA SilentlyContinue)
}} |
Format-Table -AutoSize

I rewrote the script using PSObject as initially suggested but now I do not know how to add the ForegroundColor in the if..else statement.
$infile = Import-Csv "~\Env_IPlist.csv" -Delimiter ","
$arr1 = #()
$IP_Addresses = $infile |
Select-Object Device_Type, Device_Name, IP_Address, IP_Role,
Location, Status
foreach ($IP in $IP_Addresses) {
if (Test-Connection $IP.IP_Address -Quiet -Count 1 -ErrorAction SilentlyContinue) {
$PSObject = New-Object PSObject -Property #{
Device_Type = $IP.Device_Type
Device_Name = $IP.Device_Name
IP_Address = $IP.IP_Address
IP_Role = $IP.IP_Role
Location = $IP.Location
Status = "Successful"
}
} else {
$PSObject = New-Object PSObject -Property #{
Device_Type = $IP.Device_Type
Device_Name = $IP.Device_Name
IP_Address = $IP.IP_Address
IP_Role = $IP.IP_Role
Location = $IP.Location
Status = "Failed"
}
}
$arr1 += $PSObject
}
$arr1 | Format-Table -AutoSize

Related

Adding NSLookup to Ping Script

I have this ping script that works well for what I need. I want to add to this but not sure how. I want it to output like a NSLookup. Sometimes I have the host name for the printer and would like it to output the IP if one is found and add that to another column.
First EX:
Get-Content "C:\Users\Username\Desktop\New folder\hnames.txt" | ForEach-Object { #Change User to your name and after desktop where you have IPs you want to ping
if(Test-Connection $_ -Count 1 -ErrorAction SilentlyContinue){
$status = 'Alive'
}
else {
$status = 'Dead'
}
$dns = Resolve-DnsName $_ -DnsOnly -ErrorAction SilentlyContinue
# output 1 object with two separate properties
[pscustomobject]#{
Status = $status
Target = $_
IPAddress = $dns.IPAddress -join ', '
}
} |Export-Csv 'C:\Users\Username\Desktop\New folder\output.csv' -NoTypeInformation #change this to where you want the export to go, Change Output to what you want to save as
Second EX:
Get-Content "C:\Users\Username\Desktop\New folder\hnames.txt" | ForEach-Object { #Change User to your name and after desktop where you have IPs you want to ping
if(Test-Connection $_ -Count 1 -ErrorAction SilentlyContinue){
$status = 'Alive'
}
else {
$status = 'Dead'
}
# output 1 object with two separate properties
[pscustomobject]#{
Status = $status
Target = $_
IPAddress = $success.Address.IPAddressToString
}
} |Export-Csv 'C:\Users\Username\Desktop\New folder\output.csv' -NoTypeInformation #change this to where you want the export to go, Change Output to what you want to save as
If you're doing hostname to IP Address you could use the output from Test-Connection already, for example:
Get-Content ..... | ForEach-Object {
$status = 'Dead'
if($success = Test-Connection $_ -Count 1 -ErrorAction SilentlyContinue) {
$status = 'Alive'
}
[pscustomobject]#{
Status = $status
Target = $_
IPAddress = $success.Address.IPAddressToString
}
} | Export-Csv ..... -NoTypeInformation
Another option is to use Resolve-DnsName:
Get-Content ..... | ForEach-Object {
$status = 'Dead'
if(Test-Connection $_ -Count 1 -ErrorAction SilentlyContinue) {
$status = 'Alive'
}
$dns = Resolve-DnsName $_ -ErrorAction SilentlyContinue
[pscustomobject]#{
Status = $status
Target = $_
IPAddress = $dns.IPAddress -join ', '
}
} | Export-Csv ..... -NoTypeInformation

How to display the variable result as table in email body or console screen?

The below script to get the logon users and send as email was working great but only on the console output only.
I am trying to get the result as a table so the result in the console and the email body will be like:
Server, ConnectionType, User, ID, State
PRDSVR16, rdp-tcp#44, SVC-SQL, 4, Active
PRDSVR10, rdp-tcp#27, Admin.domain, 6, Disc
SVR25-VM,console,domain.admin,8,Active
Open in new window
This is the script:
$Today = Get-Date -Format 'F'
$SessionList = "`n`nRDP Session List - " + $Today + "`n`n"
$CurrentSN = 0
# Get a list of servers from Active Directory
write-progress -activity "Getting list of servers from Active Directory" -status "... please wait ..."
$Servers = (Get-ADComputer -Filter { Enabled -eq $True -and OperatingSystem -like "*Server*" } -Properties OperatingSystem -SearchBase "OU=Data Center,DC=Company,DC=com") |
Where-Object { Test-Connection $_.Name -Count 1 -Quiet } |
Select-Object -ExpandProperty Name
$NumberOfServers = $Servers.Count
# Iterate through the retrieved list to check RDP sessions on each machine
ForEach ($Server in $Servers)
{
Write-Host "Processing $Server ..." -ForegroundColor Yellow
Write-progress -activity "Checking RDP Sessions" -status "Querying $Server" -percentcomplete (($CurrentSN / $NumberOfServers) * 100)
try
{
$SessionList += qwinsta /server:$Server |
Select-Object -Skip 1 |
% {
[PSCustomObject] #{
Type = $_.Substring(1, 18).Trim()
User = $_.Substring(19, 20).Trim()
ID = $_.Substring(41, 5).Trim()
State = $_.Substring(48, 6).Trim()
}
} |
? { $_.Type -notin 'console', 'services', 'rdp-tcp' -and $_.User -ne $null -and $_.User -ne 65536 } |
% {
"`n$Server logged in by $($_.User) on $($_.Type), session id $($_.ID) $($_.state)"
}
}
catch
{
$SessionList += "`n Unable to query " + $Server
write-host "Unable to query $Server! `n $($Error[0].Exception)" -foregroundcolor Red
}
$CurrentSN++
}
# Send the output the screen.
$SessionList + "`n`n"
$sendMailArgs = #{
From = "$env:USERNAME#$env:userdnsdomain"
To = 'SOC#domain.com'
SmtpServer = 'SMTP.domain.com'
Priority = 'High'
Body = $SessionList | Select-Object #{ N = 'Server'; E = { $Server } },
#{ N = 'User'; E = { $_.User } },
#{ N = 'LogonType'; E = { $_.Type } },
#{ N = 'ID'; E = { $_.ID } },
#{ N = 'State'; E = { $_.State } }
Subject = "$($SessionList.Count) Logged On users from $($NumberOfServers) online servers as at $($Today)"
}
Send-MailMessage #sendMailArgs
Rendering collected information in different places is way easier if you keep strict separation between data and presentation (or formatting) of said data.
For the $SessionList for example, that means doing less than what you're currently trying to do inside the loop:
$ErrorList = #()
$SessionList = foreach($server in $servers){
try{
qwinsta /server:$Server |Select-Object -Skip 1 |ForEach-Object {
[PSCustomObject] #{
Server = $server
Type = $_.Substring(1, 18).Trim()
User = $_.Substring(19, 20).Trim()
ID = $_.Substring(41, 5).Trim()
State = $_.Substring(48, 6).Trim()
}
} |Where-Object { $_.Type -notin 'console', 'services', 'rdp-tcp' -and $_.User -ne $null -and $_.User -ne 65536 }
}
catch{
$ErrorList += [pscustomobject]#{
Server = $server
ErrorRecord = $_
}
}
}
Notice how I don't construct any strings - I just create the custom objects, filter them - and then leave them as-is.
Now it becomes much easier to format the data as desired for different output media:
# For console output, simply pipe to Format-Table
$SessionList |Format-Table
if($ErrorList.Count -gt 0){
Write-Warning "The following servers had errors, please inspect"
$ErrorList |Format-Table
}
# For email output we can use `ConvertTo-Html`
$Body = $SessionList |ConvertTo-Html -As Table -Fragment
if($ErrorList.Count -gt 0){
$ErrorTable = $ErrorList |ConvertTo-Html -As Table -Fragment
$Body = $Body,$ErrorTable -join '<br />'
}
$sendMailArgs = #{
# ...
Body = ConvertTo-Html -Body $Body -Title "Session list"
BodyAsHtml = $true
# ...
}

Powershell change/edit value of Object in Variable

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!

Write Informations into existing csv

My existing csv looks like this:
"Name","Surename","Workstation"
"Doe","John","PC1"
"Fonzarelli","Arthur","PC4"
"Tribbiani","Joey","PC77"
Now, I want to check whether the host is online or not and write a new column named "Status" with the result into my csv.
$file = Import-Csv "C:\Scripts\WS-Status.csv"
Foreach ($ws in $file) {
If (Test-Connection $ws.Workstation -Count 1 -Quiet) {
Write-Host $ws.Workstation "is online"
}
Else {Write-Host $ws.Workstation "is offline"}
}
It works fine in the console, but how the heck can I export the result into my csv?
You could use the Add-Member cmdlet to add a member (status) to your current entity:
$file = Import-Csv "C:\Scripts\WS-Status.csv"
Foreach ($ws in $file)
{
if (Test-Connection $ws.Workstation -Count 1 -Quiet)
{
Add-Member -InputObject $ws -NotePropertyName "Status" -NotePropertyValue "online"
}
else
{
Add-Member -InputObject $ws -NotePropertyName "Status" -NotePropertyValue "offline"
}
}
$file | Export-Csv "C:\Scripts\WS-Status.csv" -NoTypeInformation
You could create a PSCustomObject within yourForEach loop:
$file = Import-Csv "C:\Scripts\WS-Status.csv"
Foreach ($ws in $file) {
if(Test-Connection $ws.Workstation -Count 1 -Quiet){
Write-Host $ws.Workstation "is online"
$status = "Online"
}else{
Write-Host $ws.Workstation "is offline"
$status = "Offline"
}
[array]$result += [PSCustomObject] #{
Name = $ws.Name
Surename = $ws.Surename
Workstation = $ws.Workstation
Status = $status
}
}
$result | Export-Csv thing.csv -NoTypeInformation
You have to use Out-File cmdlet which enables you to send pipelined output directly to a text file.
$file = Import-Csv "C:\Scripts\WS-Status.csv"
Foreach ($ws in $file) {
If (Test-Connection $ws.Workstation -Count 1 -Quiet) {
$ws.Workstation + " is online" | Out-File -Append OutPutFile.csv
}
Else {
$ws.Workstation + ' is offline' | Out-File -Append OutPutFile.csv
}
}

How do I calculate average ping response time in Powershell using Test-Connection?

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