Powershell script repeats same server multiple times - powershell

I have a script that runs through servers and prints out if they can connect to specific ports or not. The problem seems to be that it keeps on looping through existing servers its already done...hence I see multiple servers results and the CSV file keeps on getting re-written as each iteration restarts the cycle again to add on the next server.
Any ideas on ensuring it just cycles through one server at a time and not repeating the same server each time? The end CSV is fine as it contains the total servers but its taking ages to get there.
Output is below - you can see how it keeps on re-doing exisitng servers and just adding on one server more each time it runs:
Destination Port 3389 Port 445 Port 80
----------- --------- -------- -------
10.104.122.21 Failure Failure Failure
10.104.122.50 Failure Failure Failure
10.104.125.237 Failure Failure Failure
Destination Port 3389 Port 445 Port 80
----------- --------- -------- -------
10.104.122.21 Failure Failure Failure
10.104.122.50 Failure Failure Failure
10.104.125.237 Failure Failure Failure
10.104.125.66 Failure Failure Failure
Destination Port 3389 Port 445 Port 80
----------- --------- -------- -------
10.104.122.21 Failure Failure Failure
10.104.122.50 Failure Failure Failure
10.104.125.237 Failure Failure Failure
10.104.125.66 Failure Failure Failure
10.104.125.95 Failure Failure Failure
The code is below:
Function Test-PortConnections {
[CmdletBinding()]
# Parameters used in this function
Param
(
[Parameter(Position=0, Mandatory = $True, HelpMessage="Provide destination source", ValueFromPipeline = $true)]
$Destination,
[Parameter(Position=1, Mandatory = $False, HelpMessage="Provide port numbers", ValueFromPipeline = $true)]
$Ports = "80"
)
$ErrorActionPreference = "SilentlyContinue"
$Results = #()
ForEach($D in $Destination){
# Create a custom object
$Object = New-Object PSCustomObject
$Object | Add-Member -MemberType NoteProperty -Name "Destination" -Value $D
Write-Verbose "Checking $D"
ForEach ($P in $Ports){
#write-host "Port is $p"
$timeout=100
$requestCallback = $state = $null
$client = New-Object System.Net.Sockets.TcpClient
$beginConnect = $client.BeginConnect($d,$p,$requestCallback,$state)
Start-Sleep -milli $timeOut
if ($client.Connected -eq "True") { $Result = "True" } else { $Result = "False" }
$client.Close()
If($Result -eq "False"){ $status = "Failure" } else { $status = "Success" }
$Object | Add-Member Noteproperty "$("Port " + "$p")" -Value "$($status)"
}
$Results += $Object
If($Results){
$date = $(get-date -f yyyy-MM-dd)
$Results | Format-Table -AutoSize
$Results | Export-Csv -NoTypeInformation -Delimiter "," -Path H:\MyDocuments\Scripts\server_check_$($date).csv
}
}
}
Test-PortConnections -Destination (Get-Content -Path "H:\MyDocuments\Scripts\servers.txt") -Ports 3389,445,80

Every time the outer foreach() loop runs, at the end you:
Add current object to existing results ($Results += $object)
Format and write ALL existing results to the screen ($Results | Format-Table -AutoSize)
Export ALL the existing results to file ($Results | Export-Csv ...)
Export-Csv will simply overwrite the previously written file, so there you won't see a difference in the final result.
To fix this, I'd suggest simply having your function output $object on its own as soon as you reach the end of the loop body, and then either move the Export-Csv step outside the loop:
ForEach ($D in $Destination) {
# Create a custom object
$Object = New-Object PSCustomObject
$Object | Add-Member -MemberType NoteProperty -Name "Destination" -Value $D
Write-Verbose "Checking $D"
ForEach ($P in $Ports) {
#write-host "Port is $p"
$timeout = 100
$requestCallback = $state = $null
$client = New-Object System.Net.Sockets.TcpClient
$beginConnect = $client.BeginConnect($d, $p, $requestCallback, $state)
Start-Sleep -milli $timeOut
if ($client.Connected -eq "True") { $Result = "True" } else { $Result = "False" }
$client.Close()
If ($Result -eq "False") { $status = "Failure" } else { $status = "Success" }
$Object | Add-Member Noteproperty "$("Port " + "$p")" -Value "$($status)"
}
Write-Output $Object
$Results += $object
}
If ($Results) {
$date = $(Get-Date -f yyyy-MM-dd)
$Results | Export-Csv -NoTypeInformation -Delimiter "," -Path H:\MyDocuments\Scripts\server_check_$($date).csv
}
...or use Export-Csv with -Append, at which point you don't need $Results at all:
$date = Get-Date -Format yyyy-MM-dd
$ExportFileName = 'H:\MyDocuments\Scripts\server_check_${date}.csv'
ForEach ($D in $Destination) {
# Create a custom object
$Object = New-Object PSCustomObject
$Object | Add-Member -MemberType NoteProperty -Name "Destination" -Value $D
Write-Verbose "Checking $D"
ForEach ($P in $Ports) {
#write-host "Port is $p"
$timeout = 100
$requestCallback = $state = $null
$client = New-Object System.Net.Sockets.TcpClient
$beginConnect = $client.BeginConnect($d, $p, $requestCallback, $state)
Start-Sleep -milli $timeOut
if ($client.Connected -eq "True") { $Result = "True" } else { $Result = "False" }
$client.Close()
If ($Result -eq "False") { $status = "Failure" } else { $status = "Success" }
$Object | Add-Member Noteproperty "$("Port " + "$p")" -Value "$($status)"
}
Write-Output $object
$object | Export-Csv -Append -NoTypeInformation -Delimiter "," -Path $ExportFileName
}

Related

Update PowerShell Script to Check Remote Services

I am working on a server validation script that runs via PowerShell, and reaches out to collect remote machine information (pulls the list of servers from a text file), such as server uptime and pending reboot. It takes the information, places it in a CSV file and posts it to a HTML (Results) document.
My ask: I'm trying to reach out to these servers (contained on the .txt file) to validate that certain services are in the running state, and if they are, post the status as 'Running' and if not (else), post as 'Not Running'.
Example of getting the list of servers:
$title = 'Important! Please Read'
$message = 'Is this validation going to run against production servers?'
$choice = #(
[System.Management.Automation.Host.ChoiceDescription]::new(
'&Yes', 'This will execute against Production servers' # => This is help message
)
[System.Management.Automation.Host.ChoiceDescription]::new(
'&No', 'This will execute against Non-Production servers' # => This is help message
)
)
$defaultCoice = 0 # => No
$userinput = $host.UI.PromptForChoice($title, $message, $choice, $defaultCoice)
$prodserverlist = if($userinput -eq 0) {
Get-Content '\\networkshare\PostPatchingValidation\ServerListProd.txt'
}
else {
Get-Content '\\networkshare\PostPatchingValidation\ServerListDev.txt'
}
Example of Check Uptime:
Function Uptime($comp){
function WMIDateStringToDate($Bootup) {
[System.Management.ManagementDateTimeconverter]::ToDateTime($Bootup)
}
$NameSpace = "Root\CIMV2"
$wmi = [WMISearcher]""
$wmi.options.timeout = '0:0:10' #set timeout to 10 seconds
$query = 'Select * from Win32_OperatingSystem'
$wmi.scope.path = "\\$comp\$NameSpace"
$wmi.query = $query
Try{
$wmiresult = $wmi.Get()
#$wmiresult
foreach ($wmioutput in $wmiresult){
$Bootup = $wmioutput.LastBootUpTime
$LastBootUpTime = WMIDateStringToDate($Bootup)
$now = Get-Date
$Reporttime = $now - $lastBootUpTime
$d = $Reporttime.Days
$h = $Reporttime.Hours
$m = $Reporttime.Minutes
$a = "Up for: {0} Days, {1} Hours, {2:N0} Minutes" -f $d,$h,$m
return $a
}
}
Example of Posting results to CSV:
foreach($comp in $prodserverlist){
$i++
$ErrorActionPreference = "SilentlyContinue"
Write-Progress -Activity "Server Health Check " -Status ("Checking Server : {0}" -f $comp) -PercentComplete ($i/$prodserverlist.count*100) -Id 0
$ErrorActionPreference = "Continue"
#region Var_Nulling :p
$autoservices= $null
$Reporttimestatus = $null
$service = $null;
$services = $null;
$totalfailednew = $null
#endregion
$Reporttimestatus = uptime -comp $comp
# $services = Get-Service -comp $comp
$pd = PendingReboot $comp
$newobj = $null
$newobj = new-object psobject
$newobj | add-member -membertype noteproperty -name "Server" -value $comp
$newobj | add-member -membertype noteproperty -name "Uptime" -value $Reporttimestatus #-PassThru
$newobj | add-member -membertype noteproperty -name "PendingReboot" -value $pd
$newobj | add-member -membertype noteproperty -name "ServiceCheck" -value $Reporttimestatus
$newobj | ConvertTo-Csv -NoTypeInformation | Out-File "\\networkshare\PostPatchingValidation\result.csv" -Append
$htmlserver = $newobj.Server
$htmluptime = $newobj.Uptime
$htmlpendingreboot = $newobj.PendingReboot
$htmlservicecheck = $newobj.ServiceCheck
$current = "
<tr bgcolor=#CCCCCC>
<td width='14%' align='center'>$htmlserver</td>
<td width='23%' align='center'>$htmluptime</td>
<td width='12%' align='center'>$htmlpendingreboot</td>
<td width='12%' align='center'></td>
</tr>
"
$total += $current
#$newobj | ConvertTo-html -Fragment
#$newobj | ConvertTo-html -Fragment -CssUri \\networkshare\PostPatchingValidation\Style.css | Out-File \\networkshare\PostPatchingValidation\result.html -Append
}
$HTMLEnd = #"
</div>
</body>
</html>
"#
$MainHtml= $html + $total + $HTMLEnd
$MainHtml | Out-File "\\networkshare\PostPatchingValidation\result.html"
Final Screenshot of HTML Document:

Array of psobjects missing members in scanning TCP and UDP ports script

I'm running this script which scans ports, outputs the result to a psobject. It first checks if the port is TCP or UDP, then it runs a switch depending on whether the port is 3389, 443 or something else. If it's 3389 or 443, it uses the get-portcertificate function to get the certificate's subject and add this to the members of the object.
The problem is, when the script runs, I can see from "$obj | ft" line that it has added the RDP Cert and SSL Cert members to the object, but at the "$objServersList | ft" line these two are missing. Is it something to do with the fact that some instances of $obj have the "SSL Cert" member but some only have "RDP Cert"? How do I make it 'merge' those two types of object (or just put a null value if not appropriate)
I'm using Caleb Keene's get-portcertificate function, my code is below the function.
Function Get-PortCertificate {
<#
.SYNOPSIS
Returns certificate information from a listening TLS/SSL service port.
.DESCRIPTION
Gets the associated certificate from a TLS/SSL application service port.
.PARAMETER Computername
Hostname or IP address of the target system (Default: localhost). The function uses the supplied computername to validate with the certificate's subject name(s).
.PARAMETER Port
Port to retrieve SSL certificate (Default: 443).
.PARAMETER Path
Directory path to save SSL certificate(s).
.PARAMETER DownloadChain
Save all chain certificates to file. A certificate chain folder will be created under the specfied -path directory. -DownloadChain is dependent on the path parameter.
.NOTES
Name: Get-PortCertificate
Author: Caleb Keene
Updated: 08-30-2016
Version: 1.2
.EXAMPLE
Get-PortCertificate -Computername Server1 -Port 3389 -Path C:\temp -verbose
.EXAMPLE
"server1","server2","server3" | Get-PortCertificate
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false, ValueFromPipeline = $true, Position = 0)]
[Alias('IPAddress','Server','Computer')]
[string]$ComputerName = $env:COMPUTERNAME,
[Parameter(Mandatory = $false,Position = 1)]
[ValidateRange(1,65535)]
[int]$Port = 443,
[Parameter(Mandatory = $false)]
[ValidateNotNullorEmpty()]
[string]$Path
)
#use a dynamic parameter to prevent -downloadchain without -path.
DynamicParam {
#Need some sort of conditional check before allowing Dynamic Parameter
If ($PSBoundParameters.ContainsKey('Path')) {
#Same as [Parameter()]
$attribute = new-object System.Management.Automation.ParameterAttribute
$attribute.Mandatory = $false
$AttributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
$AttributeCollection.Add($attribute)
#Build out the Dynamic Parameter
# Need the Parameter Name, Type and Attribute Collection (Built already)
$DynamicParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("DownloadChain", [switch], $AttributeCollection)
$ParamDictionary = new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
$ParamDictionary.Add("DownloadChain", $DynamicParam)
return $ParamDictionary
}
}
Begin{
#make sure the version is supported
if ($psversiontable.psversion.Major -le 2 ){
Write-warning "Function requires PowerShell version 3 or later."
break
}
#add a custom type name to control our objects default display properties
try{ Update-TypeData -TypeName 'Get.PortCertificate' -DefaultDisplayPropertySet Subject,Issuer,NotAfter,NotBefore,ExpiresIn,CertificateValidNames,TargetName,TargetNameStatus,TargetNameStatusDetails,TargetNameIsValid,ChainPath,ChainStatus,ChainStatusDetails,CertificateIsValid -ErrorAction stop}
catch{}
#validate that the path is a filesystem directory
if ($path) {
if(-not(test-path -PathType Container FileSystem::$path)){
Write-warning "The supplied directory path is not valid: $path"
break
}
}
}
Process {
#make sure we are able to establish a port connection
#Set our connection timeout
$timeout = 1000
#Create object to test the port connection
$tcpobject = New-Object System.Net.Sockets.TcpClient
#Connect to remote port
$connect = $tcpobject.BeginConnect($ComputerName,$Port,$null,$null)
#Configure connection timeout
$wait = $connect.AsyncWaitHandle.WaitOne($timeout,$false)
If (-NOT $Wait) {
Write-Warning "[$($ComputerName)] Connection to port $($Port) timed out after $($timeout) milliseconds"
return
} Else {
Try {
[void]$tcpobject.EndConnect($connect)
Write-Verbose "[$($ComputerName)] Successfully connected to port $($Port). Good!"
} Catch {
Write-Warning "[$($ComputerName)] $_"
return
}
}
#Note: This also works for validating the port connection, but the default timeout when unable to connect is a bit long.
<#
try {
(New-Object system.net.sockets.tcpclient -ArgumentList $computername,$port -ErrorAction stop).Connected
}
catch{
Write-Warning ("Unable to connect to {0} on port {1}"-f$ComputerName,$Port)
return
}
#>
Write-Verbose "[$($ComputerName)] Getting SSL certificate from port $($Port)."
#create our webrequest object for the ssl connection
$sslrequest = [Net.WebRequest]::Create("https://$ComputerName`:$port")
$sslrequest.Timeout = 100000
#make the connection and store the response (if any).
try{$Response = $sslrequest.GetResponse()}
catch{}
#load the returned SSL certificate using x509certificate2 class
if ($certificate = [Security.Cryptography.X509Certificates.X509Certificate2]$sslrequest.ServicePoint.Certificate.Handle){
Write-Verbose "[$($ComputerName)] Certificate found! Building certificate chain information and object data."
#build our certificate chain object
$chain = [Security.Cryptography.X509Certificates.X509Chain]::create()
$isValid = $chain.Build($certificate)
#get certificate subject names from our certificate extensions
$validnames = #()
try{[array]$validnames += #(($certificate.Extensions | ? {$_.Oid.Value -eq "2.5.29.17"}).Format($true).split("`n") | ? {$_} | % {$_.split("=")[1].trim()})}catch{}
try{[array]$validnames += #($certificate.subject.split(",")[0].split("=")[1].trim())}catch{}
#validate the target name
for($i=0;$i -le $validnames.count - 1;$i++){
if ($validnames[$i] -match '^\*'){
$wildcard = $validnames[$i] -replace '^\*\.'
if($computername -match "$wildcard$"){
$TargetNameIsValid = $true
break
}
$TargetNameIsValid = $false
}
else{
if($validnames[$i] -match "^$ComputerName$"){
$TargetNameIsValid = $true
break
}
$TargetNameIsValid = $false
}
}
#create custom object to later convert to PSobject (required in order to use the custom type name's default display properties)
$customized = $certificate | select *,
#{n="ExtensionData";e={$_.Extensions | % {#{$_.oid.friendlyname.trim()=$_.format($true).trim()}}}},
#{n="ResponseUri";e={if ($Response.ResponseUri){$Response.ResponseUri}else{$false}}},
#{n="ExpiresIn";e={if((get-date) -gt $_.NotAfter){"Certificate has expired!"}else{$timespan = New-TimeSpan -end $_.notafter;"{0} Days - {1} Hours - {2} Minutes" -f $timespan.days,$timespan.hours,$timespan.minutes}}},
#{n="TargetName";e={$ComputerName}},
#{n="CertificateValidNames";e={$validnames}},
#{n="ChainPath";e={$count=0;$chaincerts = #($chain.ChainElements.certificate.subject);$($chaincerts[($chaincerts.length -1) .. 0] | % {"{0,$(5+$count)}{1}" -f "---",$_;$count+=3}) -join "`n"}},
#{n="ChainCertificates";e={#{"Certificates"=$chain.ChainElements.certificate}}},
#{n="ChainStatus";e={if($isvalid -and !$_.chainstatus){"Good"}else{$chain.chainstatus.Status}}},
#{n="ChainStatusDetails";e={if($isvalid -and !$_.chainstatus){"The certificate chain is valid."}else{$chain.chainstatus.StatusInformation.trim()}}},
#{n="CertificateIsValid";e={$isValid}},
#{n="TargetNameIsValid";e={$TargetNameIsValid}},
#{n="TargetNameStatus";e={if($TargetNameIsValid){"Good"}else{"Invalid"}}},
#{n="TargetNameStatusDetails";e={if($TargetNameIsValid){"The target name appears to be valid: $computername"}else{"TargetName $computername does not match any certificate subject name."}}}
#get object properties for our PSObject
$objecthash = [Ordered]#{}
($customized | Get-Member -MemberType Properties).name | % {$objecthash+=#{$_=$customized.$_}}
#create the PSObject
$psobject = New-Object psobject -Property $objecthash
#add the custom type name to the PSObject
$psobject.PSObject.TypeNames.Insert(0,'Get.PortCertificate')
#save our certificate(s) to file if applicable
if ($path){
write-verbose "Saving certificate(s) to file."
try {
$psobject.RawData | Set-Content -Encoding Byte -Path "$path\Cert`_$ComputerName`_$port`.cer" -ErrorAction stop
write-verbose "Certificate saved to $path\Cert`_$ComputerName`_$port`.cer."
}
catch{write-warning ("Unable to save certificate to {0}: {1}" -f "$path\Cert`_$ComputerName`_$port`.cer",$_.exception.message)}
if($PSBoundParameters.ContainsKey('DownloadChain')){
New-Item -ItemType directory -path "$path\ChainCerts`_$ComputerName`_$port" -ErrorAction SilentlyContinue > $null
$psobject.chaincertificates.certificates | % {
try {
Set-Content $_.RawData -Encoding Byte -Path "$path\ChainCerts`_$ComputerName`_$port\$($_.thumbprint)`.cer" -ErrorAction stop
write-verbose "Certificate chain certificate saved to $path\ChainCerts`_$ComputerName`_$port\$($_.thumbprint)`.cer."
}
catch{
write-warning ("Unable to save certificate chain certificate to {0}: {1}" -f "$path\ChainCerts`_$ComputerName`_$port",$_.exception.message)
}
}
}
}
#abort any connections
$sslrequest.abort()
#return the object
$psobject
}
else{
#we were able to connect to the port but no ssl certificate was returned
write-warning ("[{0}] No certificate returned on port {1}."-f $ComputerName,$Port)
#abort any connections
$sslrequest.abort()
return $false
}
}
}
$ComputerName = import-csv "C:\TEMP\Failed Comp.csv" | select -ExpandProperty computer
$PortArray = '22 TCP',
'53 UDP',
'80 TCP',
'3389 TCP',
'443 TCP'
$objServersList = #()
Foreach ($Computer in $ComputerName){
$obj = #()
$obj = new-object psobject
$obj | add-member -name Computer -type noteproperty -value $Computer
foreach ($Port in $PortArray){
$Port1,$Port2 = $port.split(" ")
if ($Port2 -eq 'TCP'){
switch($Port1){
'3389'
{
$TestConnection = Test-NetConnection -ComputerName $Computer -Port $Port1
if($TestConnection.TcpTestSucceeded){
$Result = 'SUCCESS'
$obj | add-member -name $Port -type noteproperty -value $Result
$RdpCert = (Get-PortCertificate -ComputerName $Computer -port 3389) #
if($RdpCert -ne $false){ #sometimes 3389 is open but it's still unable to get a cert, this prevents it throwing an error.
$RdpCertString = ($RdPCert.subject).trimstart('CN=')
$obj | add-member -name 'RDP Cert' -type noteproperty -value $RdpCertString
}
}
else{
$Result = 'FAILURE'
$obj | add-member -name $Port -type noteproperty -value $result
}
}
'443'
{
$TestConnection = Test-NetConnection -ComputerName $Computer -Port $Port1
if($TestConnection.TcpTestSucceeded){
$Result = 'SUCCESS'
$obj | add-member -name $Port -type noteproperty -value $Result
$SslCert =(Get-PortCertificate -ComputerName $Computer -port 443)
if($SslCert -ne $false){ #sometimes 443 is open but it's still unable to get a cert, this prevents it throwing an error.
$SslCertString = ($SslCert.subject).replace(', ',' | ')
$obj | add-member -name 'SSL Cert' -type noteproperty -value $SslCertString
}
}
else{
$Result = 'FAILURE'
$obj | add-member -name $Port -type noteproperty -value $result
}
}
Default
{
$TestConnection = Test-NetConnection -ComputerName $Computer -Port $Port1
if($TestConnection.TcpTestSucceeded){
$Result = 'SUCCESS'
$obj | add-member -name $Port -type noteproperty -value $Result
}
else{
$Result = 'FAILURE'
$obj | add-member -name $Port -type noteproperty -value $Result
}
}
}
}
elseif($Port2 -eq 'UDP'){
$result = if((test-port -computer $computer -port $port1).open -eq $true){write-output "SUCCESS"}else{write-output "FAILURE"}
$obj | add-member -name $Port -type noteproperty -value $result
}
else{
$obj | add-member -name $Port -type noteproperty -value "Invalid protocol"
}
}
$obj | ft
$objServersList+=$obj
}
$objServersList | ft
Format-Table decides the table header format based on the first few items piped to it, so if only some of the objects have an RDP Cert property and the first such object is not at the start of the list, it won't be shown.
The solution is to either always add the property to all objects regardless of whether it has a value or not, or by requesting Format-Table show the property explicitly:
$objServersList | ft Computer,'22 TCP','53 UDP','80 TCP','3389 TCP','RDP Cert','443 TCP'

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!

Hashtables and RunSpaces in powershell

I am having some problems with updating data in a list from the results of a hash table. I am 99% sure it is due to the lack of understanding of what i am doing.
I am generating a $list of servers from a CSV. the CSV contains Servername, domain, description, plus some additional blank columns for use later.
what i am trying to do in a nutshell: i need to pull the down processes from a list of remote servers. to do this i am throwing each server from the list and function into its own runspace, the Hashtable is updating as expected. But i can not update the original $list i have.
here is my code:
Function OpenFile ($FilePath) {
$OFDiag = new-object system.windows.forms.openfiledialog
$OFDiag.filter = 'CSV (*.csv) | *.csv'
$OFDiag.ShowDialog() | out-null
$OFDiag.filename
}
# Create Primary Variables
$FilePath = OpenFile
$list = (get-content $FilePath) -replace '\(',' -' #ALL Servers and Groups need to remove parenthesis
$list = $list -replace '\)' #finish up removing the parenthesis
$list = $list -replace ' Or WorkGroup'
$list = convertFrom-CSV $list | select 'Name', 'Computer_Description', 'Domain' #Need to convert the list into a CSV formatted table.
$list = $list | sort Name
$list | Add-Member -NotePropertyName 'LastReboot' -NotePropertyValue $null
$list | Add-Member -NotePropertyName 'LastDeployment' -NotePropertyValue $null
$list | Add-Member -NotePropertyName 'RebootStatus' -NotePropertyValue $null
$list | Add-Member -NotePropertyName 'DownProcess' -NotePropertyValue $null
$list | Add-Member -NotePropertyName 'EnabledStatus' -NotePropertyValue $null
$list | Add-Member -NotePropertyName 'RDP' -NotePropertyValue $null
$list | Add-Member -NotePropertyName 'SchedTask' -NotePropertyValue $null
$servers = $list | %{$_.Name} | sort #ALL SERVERS - ONLY Servernames
$ServProSel = {
#
# Checks for Running Services and Processes.
# This Makes a determination as to what service/process groups should be checked.
# The Information as to what processes to look for are sent to the ProSer_Check function
# information from there is sent to the ServerStatus Tab
#
#Write-Host 'starting ServerProSel'
Param ($computer,$cred,$grpName,$hash)
#$cred = $(get-Variable "$Domain" -valueOnly)
$ck =#{} #$(Get-Variable -name "SCP_$serName" -ValueOnly)
Function ProSer_Check {
# This is the actual function that is run on the remote system to check
# for processes and services.
param ( [array] $Prcs,
[string] $Computer )
$script:chkres =#()
foreach ($p in $Prcs){
$script:res = Get-Process -name $p -ErrorAction SilentlyContinue
if (!$res) {
$chk = "$p -DOWN`r`n"
$chkres += $chk
}
}
if ($chkres.count -eq 0){
$chkres = "All Processes Up"}
Return $chkres
}
switch -Regex ($grpName){
'Demonstration' {
$Prcs = #('Process.Service'); break}
'Historian' {
$Prcs =#('Process.Service'); break}
'Models' {
$Prcs =#('UpdaterServer'); break}
'Inflictor' {
$Prcs =#('Automation.EngineService','Automation.Manager.Service','Automation.SmfLauncher','postgres','Redundancy.Server','WatchDog.Service'); break}
'Simulator' {
$Prcs =#('proc','moni','server','serve','clerk','web'); break}
'WebServer' {
$Prcs =#('w3wp','VShell'); break}
default {
$Prcs =#('svchost'); break}
}
$R = invoke-command -credential $cred -computername $Computer -scriptblock ${function:ProSer_Check} -ArgumentList $Prcs,$Computer
$hash[$Computer]=([string]$R)
}
$Script:runspaces = New-Object System.Collections.ArrayList
$Global:hash = [hashtable]::Synchronized(#{})
$global:sessionstate = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
$global:runspacepool = [runspacefactory]::CreateRunspacePool(1, 10, $sessionstate, $Host)
$global:runspacepool.Open()
Function SendToRunSpace {
$function = $args[0]
#$function
$powershell = [powershell]::Create().AddScript($function).AddArgument($computer.name).AddArgument($cred).AddArgument($grpName).AddArgument($hash)
$powershell.RunspacePool = $global:runspacepool
#$hash = #{Name=$computer.name;DownProcess = "Waiting.."}
$temp = "" | Select-Object PowerShell,Runspace,Computer
$Temp.Computer = $Computer
$temp.PowerShell = $powershell
$temp.Runspace = $powershell.BeginInvoke()
Write-Verbose ("Adding {0} collection" -f $temp.Computer)
$runspaces.Add($temp) | Out-Null
}
ForEach ($Computer in $list) {
$domain = $computer.Domain
$grpName = $computer.'Computer_Description'
$cred = $(get-Variable "$Domain" -valueOnly)
#Create the powershell instance and supply the scriptblock with the other parameters
if(!$(Get-Variable "TEST_$domain" -ValueOnly)){
CredCheck $computer.name $cred
}
#SendToRunSpace $scriptBlock $computer $domain $global:hash
SendToRunSpace $ServProSel $computer $cred $grpName $global:hash
}
I am running this in PowerShell ISE so i can edit on the fly and test things. When i run this code i generate the $list and $hash items. Ultimately i would like to grab the value out of the $hash for the server and update the corresponding server information in the $list object.
or is there a better way to do this? is the Hashtable the only way to Synchronize data from the runspaces to the current process?

Powershell Forloop with custom header

Desired output:
Server Data1 Data2
------ ----- -----
Server1 Stuff1 Thing1
Server2 Stuff2 Thing2
Server3 Stuff3 Thing3
Question:
I have 3-4 different variables that I want in a custom table view with a custom header. The problem is that I use a ForEach and I'm not sure how to get it into a table view. Right now the best I can do is Write-Host "$server $stuff $thing" and that puts each one in a row. I tried to some magic with #{Expression={$_.Name};Label="Name" but did not get anywhere.
The main point is that I have 3 vars at the end of the day and I need to get it into a custom table view.
$data = #("Server1", "Server2", "Server3")
while($true) {
cls
"`r"
ForEach ($server in $data) {
if (Test-Connection $server -count 1 -quiet) {
net use r: \\$server\s$\Folder /user:server\administrator password >$null
r:
$inputcount = (dir input -Recurse | Measure-Object).Count
$forwardcount = (dir forward -Recurse | Measure-Object).Count
$forwardcount = ($forwardcount-2)
if($fowardcount -lt 0) {
$forwardcount = 0
}
if($inputcount -gt 0 -or $forwardcount -gt 0) {
if($inputcount -gt 5000 -or $forwardcount -gt 200) {
$sleepless = "1"
}
Write-Host "$server" -ForegroundColor Red -NoNewline
Write-Host ": " -NoNewline
Write-Host "$inputcount" -ForegroundColor Red -NoNewline
Write-Host " - " -NoNewline
Write-Host "$forwardcount" -ForegroundColor Red
$inputcount = ""
c: >$null
net use r: /Delete >$null
} else {
Write-Host "$server" -NoNewline
Write-Host ": " -NoNewline
Write-Host "$inputcount" -NoNewline
Write-Host " - " -NoNewline
Write-Host "$forwardcount"
$inputcount = ""
c: >$null
net use r: /Delete >$null
}
}
}
}
Use a collection of custom objects:
"Server1", "Server2", "Server3" | ForEach-Object {
# NB: Test-Connection "host" -Count 1 sometimes fails on an online host
if(Test-Connection $_ -Quiet -Count 2 -ErrorAction SilentlyContinue) {
$status = "online"
} else {
$status = "offline"
}
# PowerShell v3+
[PSCustomObject]#{
Server = $_
Status = $status
Count = 20
What = "ever"
}
# PowerShell v2
$item = New-Object -TypeName PSObject
$item | Add-Member -MemberType NoteProperty -Name Server -Value $_
$item | Add-Member -MemberType NoteProperty -Name Status -Value $status
$item | Add-Member -MemberType NoteProperty -Name Count -Value 20
$item | Add-Member -MemberType NoteProperty -Name What -Value "ever"
$item
}
Returns:
Server Status Count What
------ ------ ----- ----
Server1 offline 20 ever
Server1 offline 20 ever
Server2 offline 20 ever
Server2 offline 20 ever
Server3 offline 20 ever
Server3 offline 20 ever
This is the general format I would use. The foreach is completely incorrect as I don't know how you're going from one row of data to the next; whatever it is, loop over that.
Bare in mind this is destructive; += deletes the previous array and creates a new one. However it tends to be my goto when dealing with small data sets because of simplicity and readability.
foreach ($line in $lines) {
[array]$tableVariable += New-Object PSObject -Property #{
Server = $server
Data1 = $stuff
Data3 = $thing
}
}