I've got this script, which loops through a foreach loop for each computer, and for each computer it loops through each NIC. Currently the Catch block is not running. What I want to do is catch the error (usually because get-wmi is not able to connect to a machine), do something (add some information to a PSCustomObject), but then continue to the next iteration. How do I catch an error but also continue the foreach loop?
param (
[Alias('Hostname')]
[string[]]$ComputerName = #('pc1','pc2'),
$OldDNSIP = '7.7.7.7',
$NewDNSIP = #('9.9.9.9','8.8.8.8')
)
$FailedArray = #()
$DHCPArray = #()
Foreach ($Computer in $ComputerName){
$NICList = Get-WmiObject Win32_NetworkAdapterConfiguration -computername $Computer | where{$_.IPEnabled -eq "TRUE"}
Foreach($NIC in $NICList){
If($NIC.DHCPEnabled -eq $false){
Try{
$DNSIPs = $NIC.DNSServerSearchOrder
if($DNSIPs -contains $OldDNSIP){
$NewDNS = $DNSIPs | foreach {$_ -replace $OldDNSIP,$NewDNSIP[0]}
$null = $NIC.SetDNSServerSearchOrder($NewDNS)
}
else{
write-host " - Old DNS server IP not found... ignoring"
}
}
Catch{
write-host " - Something went wrong... logging to a CSV for review later" -ForegroundColor Red
$FailedArray += [PSCustomObject]#{
'Computer' = $Nic.pscomputername
'NIC_ID' = $nic.index
'NIC_Descrption' = $nic.description}
}
}
ElseIf($NIC.DHCPEnabled -eq $true){
write-host " - DHCP is enabled. Adding this IP, Hostname, Nic Index and DHCP Server to a CSV for reviewing."
#add pscomputer, nic id, refernece and dhcp server to DHCPNICArray
$DHCPArray += [PSCustomObject]#{
'Computer' = $Nic.pscomputername
'NIC_ID' = $nic.index
'NIC_Descrption' = $nic.description
'DHCPEnabled' =$nic.dhcpenabled
'DHCPServer' = $nic.dhcpserver}
}
}
}
$DHCPArray | export-csv c:\temp\dhcp.csv -NoTypeInformation
$FailedArray | export-csv c:\temp\failed.csv -NoTypeInformation
If the WMI errors are due to a connection fail, they will occur before any of the nics are processed. If you want to catch them, and then continue with the next computer, you have to move the try-catch up to the level of that loop. If you also want to catch nic-specific errors, you need a 2nd try-catch at that level.
Also, consider using -ErrorAction Stop parameter, or specify $ErrorActionPreference = 'Stop', to make sure all errors are terminating (that means, jump right to the catch block).
Here's an example, with comments for explanation:
$ErrorActionPreference = 'Stop'
foreach ($Computer in $ComputerName) {
# add a try-catch per computer,
# to catch WMI errors
# and then continue with the next computer afterwards
try {
# if errors occur here,
# execution will jump right to the catch block the bottom
$NICList = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $Computer -ErrorAction Stop | where {
$_.IPEnabled -eq "TRUE"
}
foreach ($NIC in $NICList) {
# add a try-catch per NIC,
# to catch errors with this specific NIC and then
# continue with the next nic
try {
if ($NIC.DHCPEnabled -eq $false){
$DNSIPs = $NIC.DNSServerSearchOrder
if ($DNSIPs -contains $OldDNSIP) {
$NewDNS = $DNSIPs | foreach {$_ -replace $OldDNSIP,$NewDNSIP[0]}
$null = $NIC.SetDNSServerSearchOrder($NewDNS)
}
else {
write-host " - Old DNS server IP not found... ignoring"
}
}
elseif ($NIC.DHCPEnabled -eq $true){
write-host " - DHCP is enabled. Adding this IP, Hostname, Nic Index and DHCP Server to a CSV for reviewing."
$DHCPArray += [PSCustomObject]#{
'Computer' = $Nic.pscomputername
'NIC_ID' = $nic.index
'NIC_Descrption' = $nic.description
'DHCPEnabled' =$nic.dhcpenabled
'DHCPServer' = $nic.dhcpserver
}
}
}
catch {
write-host " - Configuring a NIC went wrong... logging to a CSV for review later" -ForegroundColor Red
# add nic-specific entry
$FailedArray += [PSCustomObject]#{
'Computer' = $Nic.pscomputername
'NIC_ID' = $nic.index
'NIC_Descrption' = $nic.description
}
}
# continue with next nic...
} # foreach nic
}
catch {
write-host " - Something else went wrong... logging to a CSV for review later" -ForegroundColor Red
# add entry for current computer
# (we don't know about nics, because wmi failed)
$FailedArray += [PSCustomObject]#{
'Computer' = $Computer
}
}
# continue with next computer...
} # foreach computer
Related
First and foremost thanks for your time and help.
My issue here is that I am having an erratic behavior in getting the name of the remote logged users. I want to send a message to a certain list of computers or IPs, and if somebody is logged get who saw the message.
To do so I created a script which should read and get the computer names one by one, and see if is connected. If so, get the name of the user, send the message and then show in table and write on a txt file the results:
#We read the "Lista" file with IPs or computer names, works with both
$PCLIST = Get-Content 'C:\Users\XXXX\Desktop\Lista.txt'
#Add the date tot he txt files to be created
$Fecha = date
echo $Fecha > "C:\Users\XXXX\Desktop\ListOffline.txt"
echo $Fecha > "C:\Users\XXXX\Desktop\Done.txt"
#We check every computer and if ONLINE send the message
foreach ($computer in $PCLIST)
{
if ((Test-NetConnection -ComputerName $computer -WarningAction SilentlyContinue).PingSucceeded -eq $true) #If ping back is succesfull then write the message
{
$Usuario = (Get-WmiObject -Class win32_computersystem -ComputerName $computer).UserName
if ((Get-WmiObject -Class win32_computersystem -ComputerName $computer).UserName) {" "} else {$Usuario = "Usuario Remoto"}
$output = #{ 'Computer_Name / IP' = $computer }
$output.Usuario_Conectado = (Get-WmiObject -Class win32_computersystem -ComputerName $computer).UserName
msg * /server:$computer "Hey!!" $Usuario ", Something meaninfull :) " #Message1
msg * /server:$computer "And something more meaningfull even ;)" #Message2
echo "$computer, avisado y recibido por $Usuario" >> "C:\Users\XXXX\Desktop\Done.txt"
}
else
{
$output = #{'Computer_Name / IP' = $computer }
$output.Usuario_Conectado = "OFFLINE"
echo $Computer >> "C:\Users\XXXX\Desktop\ListOffline.txt"
}
[PSCustomObject]$output
}
The message part works as expected and the computers, when logged, can see the messages BUT:
I can't get the names of the remote logged users on the computer's list, I get mine and the rest as empty names. In some weird variation I could, but can't find what the problem is anymore. As pre-requisites I allowed Win RM and firewall config:
I went to the computers where this will be sent and modified the registry: "reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server" /v "AllowRemoteRPC" /t "REG_DWORD" /d "1" /f"
and opened the sshnet to allow remote access to get the names: "netsh firewall set service remoteadmin enable"
In the image in the orange circles should go the remote users logged but I get empty names
I want to send the message IF the user is logged, as remote user or on the console, now the txt file of "Done" gets written even when the user don't receive any message...
So, there should be an issue on this call: "(Get-WmiObject -Class win32_computersystem -ComputerName $computer).UserName" to get the names, this worked but I can't get the names on the remote logged users computers anymore... any idea or suggestion? I can accept get those in other form if is available and easier.
Thanks and best,
Juan
If you're using command line utilities anyway, why not use quser as its designed to get the information you need:
Function FindLogons {
Param (
[string]$Computer
)
Try {
$Query = quser /server:$Computer 2>&1
If ($LASTEXITCODE -ne 0) {
$ErrorDetail = $Query.Exception.Message
Switch -Wildcard ($Query) {
'*[1722]*' {
$Status = 'Remote RPC not enabled'
}
'*[5]*' {
$Status = 'Access denied'
}
'No User exists for*' {
$Status = 'No logged on users found'
}
default {
$Status = 'Error'
}
}
$result = [pscustomobject]#{ErrorStatus = $Status;ErrorMessage = $ErrorDetail}
$result.PSObject.Properties | ForEach-Object {
Write-Host $_.Name "`t" $_.Value
}
Return
}
Else {
Write-Host "Query complete"
}
$Query | Select-Object -Skip 1 -ErrorAction Stop | ForEach-Object {
$CurrentLine = $_.Trim() -Replace '\s+',' ' -Split '\s'
# If session is disconnected different fields will be selected
If ($CurrentLine[2] -eq 'Disc') {
$SearchResult = [pscustomobject]#{
UserName = $CurrentLine[0];
SessionName = $null;
Id = $CurrentLine[1];
State = $CurrentLine[2];
IdleTime = $CurrentLine[3];
LogonTime = $CurrentLine[4..($CurrentLine.GetUpperBound(0))] -join ' '
}
# LogonTime = $CurrentLine[4..6] -join ' ';
}
Else {
$SearchResult = [pscustomobject]#{
UserName = $CurrentLine[0];
SessionName = $CurrentLine[1];
Id = $CurrentLine[2];
State = $CurrentLine[3];
IdleTime = $CurrentLine[4];
LogonTime = $CurrentLine[5..($CurrentLine.GetUpperBound(0))] -join ' '
}
}
If ($SearchResult) {
$SearchResult
}
}
}
Catch {
Write-Host "Error: $_"
}
Write-Host "Done"
}
Below function I want to pass multiple value in array. When I'm passing more than one value I am getting an error.
function CheckProcess([String[]]$sEnterComputerNameHere, [String[]]$sEnterProccessNameHere) {
#Write-Host " $sEnterComputerNameHere hello"
#($sEnterComputerNameHere) | ForEach-Object {
# Calling Aarray
#($sEnterProccessNameHere) | ForEach-Object {
if (Get-Process -ComputerName $sEnterComputerNameHere | where {$_.ProcessName -eq $sEnterProccessNameHere}) {
Write-Output "$_ is running"
} else {
Write-Output "$_ is not running"
}
}
}
}
$script:sEnterProccessNameHere = #("VPNUI") # Pass the process agreement here
$script:sEnterComputerNameHere = #("hostname") # Pass the process agreement here
CheckProcess $sEnterComputerNameHere $sEnterProccessNameHere
Give it a try with this one:
Function CheckProcess([String[]]$sEnterComputerNameHere,[String[]]$sEnterProccessNameHere)
{ #Write-host " $sEnterComputerNameHere"
#($sEnterComputerNameHere) | Foreach-Object {
$computer = $_
Write-Host $computer
#($sEnterProccessNameHere) | Foreach-Object {
$process = $_
Write-Host $process
try{
$x = get-process -computername $computer #Save all processes in a variable
If ($x.ProcessName -contains $process) #use contains instead of equals
{
Write-Output "$process is running"
}
else
{
Write-Output "$process is not running"
}
}
catch
{
Write-Host "Computer $computer not found" -ForegroundColor Yellow
}
}
}
}
$script:sEnterProccessNameHere = #("VPNUI","Notepad++","SMSS")
$script:sEnterComputerNameHere = #("remotecomputer1","remotecomputer2")
CheckProcess -sEnterComputerNameHere $sEnterComputerNameHere -sEnterProccessNameHere $sEnterProccessNameHere
In general, it would be great if you write the error you get in your question. That helps others to help you.
If I work with arrays and | Foreach, I always write the $_in a new variable. That helps if I have another | Foreach (like you had) to know for sure, with which object I'm working with..
EDIT: I changed the script, so it uses "-contains" instead of "-eq" and I added a try/catch block, so if the other computer is not found, it gives you a message.. It works on my network
EDIT2: Do you have access to the other computers? If you run get-process -computername "name of remote computer" do you get the processes?
$topDC1="10.254.90.17"
$topDC2="10.225.224.17"
$topDC3="10.110.33.32"
$topDC4="10.88.100.10"
$DomainName="office.adroot.company.net"
TRY{
$hostname = [System.Net.DNS]::GetHostByName($topDC1).HostName.toupper()
$ipaddress = [System.Net.Dns]::GetHostAddresses($DomainName) | select IPAddressToString -ExpandProperty IPAddressToString
# I want the below to loop foreach ip in the object, ns it against all 4 topDC's, then output each result :(
$NS1 = nslookup $ipaddress[0] $topDC1
Write-host $NS1
}
Catch{
write-host "error"
}
Here is my dirty code so far (just to keep it simple)
I am trying to automate this:
NSLOOKUP office.adroot.company.net
put the results into an object
for each ip in results, do an NSLOOKUP against our top level DC's.
find which DC's haven't been cleaned up after decommission (still in dns)
$DCList="10.254.90.17","10.225.224.17","10.110.33.32","10.88.100.10"
$DomainName="office.adroot.blorg.net","pcd.blorg.ca","blorg.ca","percom.adroot.blorg.net", "blorg.blorg.net","ibg.blorg.net","sacmcm.adroot.blorg.net","sysdev.adroot.blorg.net","adroot.blorg.net"
TRY{
foreach ($DomainNameItem in $DomainName){
Write-Host ""
Write-Host ""
Write-Host "Looking UP result"$DomainNameItem -foreground yellow
Write-Host ""
$hostname = [System.Net.DNS]::GetHostByName($DCListItem).HostName.toupper()
$ipaddress = [System.Net.Dns]::GetHostAddresses($DomainNameItem).IPAddressToString
foreach ($ip in $ipaddress){
Write-Host ""
Write-Host "Looking UP result"$ip -foreground green
foreach ($topdns in $DCList){
$RESULTS = nslookup $ip $topdns
Write-host $RESULTS
}
}
}
}
Catch{
write-host "error"
}
Write-Host ""
Write-Host ""
pause
Got it! This will save me tonnes of work determining if a DNS cleanup is necessary. Thanks guys, I'm learning just how great Powershell can be :)
Try this:
$topDomainControllers = #("10.254.90.17", "10.225.224.17", "10.110.33.32", "10.88.100.10")
$DomainName="office.adroot.company.net"
try {
$hostname = [System.Net.Dns]::GetHostByName($topDC1).HostName.ToUpper()
$ipAddresses = [System.Net.Dns]::GetHostAddresses($DomainName) |
select -ExpandProperty IPAddressToString
foreach ($ipAddress in $ipAddresses) {
$nslookupResult = nslookup $ipAddress
$foundIp = $nslookupResult[1] -match "^\D*(\d+\.\d+\.\d+\.\d+)$"
if ($foundIp -eq $false) {
continue
}
$domainController = $Matches[1]
if ($topDomainControllers.Contains($domainController)) {
Write-Output -Verbose "Found domain controller match for $domainController"
break
} else {
Write-Output -Verbose "No match found for domain controller $domainController"
}
}
} catch {
Write-Output "An error has occured: $_"
}
I'm currently trying to put together a script that queries AD for a list of computers, pings the computers to determine which ones are still active, and then telnets into a specific port on all the pingable computers. The output I'm looking for is a full list of pingable computers in AD for which I can't telnet to the said port.
I've read these few questions, but they don't quite hit on what I'm trying to do. I just want to see if the telnet connection is successful without entering telnet (or automate the quitting of telnet) and move on to the next machine to test. The AD and pinging portions of my script are set, I'm just stuck here. The things I've tried haven't quite worked as planned.
Here is the code for the first parts of the script, if needed:
Get-ADComputer -Filter * -SearchBase 'DC=hahaha,DC=hehehe' | ForEach {
$computerName = $_.Name
$props = #{
ComputerName = $computerName
Alive = $false
PortOpen = $false
}
If (Test-Connection -ComputerName $computerName -Count 1 -Quiet) {
$props.Alive = $true
}
Adapting this code into your own would be the easiest way. This code sample comes from the PowerShellAdmin wiki. Collect the computer and port you want to check. Then attempt to make a connection to that computer on each port using Net.Sockets.TcpClient.
foreach ($Computer in $ComputerName) {
foreach ($Port in $Ports) {
# Create a Net.Sockets.TcpClient object to use for
# checking for open TCP ports.
$Socket = New-Object Net.Sockets.TcpClient
# Suppress error messages
$ErrorActionPreference = 'SilentlyContinue'
# Try to connect
$Socket.Connect($Computer, $Port)
# Make error messages visible again
$ErrorActionPreference = 'Continue'
# Determine if we are connected.
if ($Socket.Connected) {
"${Computer}: Port $Port is open"
$Socket.Close()
}
else {
"${Computer}: Port $Port is closed or filtered"
}
# Apparently resetting the variable between iterations is necessary.
$Socket = $null
}
}
Here is a complete powershell script that will:
1. read the host and port details from CSV file
2. perform telnet test
3. write the output with the test status to another CSV file
checklist.csv
remoteHost,port
localhost,80
asdfadsf,83
localhost,135
telnet_test.ps1
$checklist = import-csv checklist.csv
$OutArray = #()
Import-Csv checklist.csv |`
ForEach-Object {
try {
$rh = $_.remoteHost
$p = $_.port
$socket = new-object System.Net.Sockets.TcpClient($rh, $p)
} catch [Exception] {
$myobj = "" | Select "remoteHost", "port", "status"
$myobj.remoteHost = $rh
$myobj.port = $p
$myobj.status = "failed"
Write-Host $myobj
$outarray += $myobj
$myobj = $null
return
}
$myobj = "" | Select "remoteHost", "port", "status"
$myobj.remoteHost = $rh
$myobj.port = $p
$myobj.status = "success"
Write-Host $myobj
$outarray += $myobj
$myobj = $null
return
}
$outarray | export-csv -path "result.csv" -NoTypeInformation
result.csv
"remoteHost","port","status"
"localhost","80","failed"
"asdfadsf","83","failed"
"localhost","135","success"
I have script:
$servers = "server01", "s02", "s03"
foreach ($server in $servers) {
$server = (New-Object System.Net.NetworkInformation.Ping).send($servers)
if ($server.Status -eq "Success") {
Write-Host "$server is OK"
}
}
Error message:
An exception occured during a Ping request.
I need to ping each server in $servers array and display status. I think, that Foreach statement is not properly used, but I'm unable to find out where is the problem. Thank you for your advice
You should not be modifying the value of $server within the foreach loop. Declare a new variable (e.g. $result). Also, Ping.Send takes the individual server name, not an array of server names as an argument. The following code should work.
Finally, you will need to trap the PingException that will be thrown if the host is unreachable, or your script will print out a big red error along with the expected results.
$servers = "server1", "server2"
foreach ($server in $servers) {
& {
trap [System.Net.NetworkInformation.PingException] { continue; }
$result = (New-Object System.Net.NetworkInformation.Ping).send($server)
if ($result.Status -eq "Success") {
Write-Host "$server is OK"
}
else {
Write-Host "$server is NOT OK"
}
}
}