Powershell extract error from a text file on different computers - powershell

I am trying to extract a list of computer having an error in a specific text file. I did the following :
$computernames = Get-Content C:\PC.txt
foreach ($server in $computernames)
{
$filepath = Test-Path "\\$server\c$\"
if ($filepath -eq "True") {
Get-Content "\\$server\c$\file.log" | Select-String "Error" -quiet | Select-Object $server
}
}
That is returning me the first computer with an error, then only some blank lines in the command window.
If I try to get it in a file with out-file or extract or whatever, I have a blank file.
Thanks in advance for any clue on the matter.
Regards.

There are multiple things you can do with this. Let me tell you first what you are looking for:
So lets say you have an error , in order to capture that you should use try/catch.
Replace :
Get-Content "\\$server\c$\file.log" | Select-String "Error" -quiet | Select-Object $server
With This:
try
{
Get-Content "\\$server\c$\file.log" | Select-String "Error" -quiet | Select-Object $server
}
catch
{
$Error >> ErrorFile.txt
}
Second thing, I would suggest is. Lets say you have 10 computers and the lets assume the 8th one is having some error. But still you do not wish to stop the loop and proceed with the remaining computers of the loop. In that case you should use 2 try/catch like this:
$computernames = Get-Content C:\PC.txt
try
{
foreach ($server in $computernames)
{
$filepath = Test-Path "\\$server\c$\"
try{
if ($filepath -eq "True") {
try
{
Get-Content "\\$server\c$\file.log" | Select-String "Error" -quiet | Select-Object $server
}
catch
{
$Error >> ErrorFile.txt
}
}
}
catch
{
$_.Exception.Message >> Errorfile.txt
}
}
}
catch
{
$_.Exception.Message >> Errorfile.txt
}
So the approach is, if there is any error inside the foreach then the internal catch block will catch it and the error message will be parsed to the errorfile.txt keeping the loop running and will proceed with the remaining computers.
If there is some issue with the main $computernames while getting the content of the file, then it will come to the main catch block with the respective error message parsed and appended in the errorfile.txt
Hope it helps you in understanding the effectiveness of try/catch

Related

Powershell Throw Causing Variables to Clear?

My PowerShell script just checks multiple servers to make sure the input* and output* directories are clear of any files.
I'm simply trying to output to console the results of a GCI call prior to throwing an error message. However, when I uncomment the "throw" line, the $inputFiles and $outputFiles no longer output to the console. Below is the code:
$allServers = #(
"server1.com",
"server2.com")
foreach ($server in $allServers) {
$inputFiles = Get-ChildItem -Path "\\$server\C$\jobs\statements\input*\" -Recurse | Where-Object {! $_.PSIsContainer } | Select FullName
$outputFiles = Get-ChildItem -Path "\\$server\C$\jobs\statements\output*\" -Recurse | Where-Object {! $_.PSIsContainer } | Select FullName
if ($inputFiles -eq $NULL -and $outputFiles -eq $NULL) {
Write-Host "Environment is ready for statement processing."
}
else {
Write-Host "Environment is NOT ready for statement processing."
Write-Host "The following files exist in input/output: `n"
$inputFiles
$outputFiles
#Throw "Files exist in input/output. See above for details."
}
}
Below is the console output:
Environment is NOT ready for statement processing.
The following files exist in input/output:
Environment is NOT ready for statement processing.
The following files exist in input/output:
FullName
--------
\\server1.com\C$\jobs\statements\input\asdasd.txt
\\server1.com\C$\jobs\statements\input_254\asdasd.txt
\\server1.com\C$\jobs\statements\input_test\asdasd.txt
\\server2.com\C$\jobs\statements\input\CUSSTAT10302021.245
\\server2.com\C$\jobs\statements\input\CUSSTAT11312021
\\server2.com\C$\jobs\statements\input\CUSSTAT11312021.zip
And below is the console output when I uncomment the "throw" line:
Environment is NOT ready for statement processing.
The following files exist in input/output:
Files exist in input/output. See above for details.
At C:\jobs\statements\bin\Statements-EnvironmentCheck.ps1:47 char:9
+ Throw "Files exist in input/output. See above for details."
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (Files exist in ...ve for details.:String) [], RuntimeException
+ FullyQualifiedErrorId : Files exist in input/output. See above for details.
I know I have some error output cleanup to perform in order to include all the servers that might have files present, but please ignore that for now.
What you're experiencing is explained in this answer and this answer, basically you need to implement Out-Host \ Out-Default:
$inputFiles, $outputFiles | Out-Host # Should fix the problem
# possibly `throw` might require this too
throw "Files exist in input/output. See above for details." | Out-Host
However, I feel is worth showing you a better way to approach your code, returning a unified array of objects which you can filter, sort and export.
$allServers = #(
"server1.com"
"server2.com"
)
$result = foreach ($server in $allServers) {
# use `-File` instead of `! $_.PSIsContainer`
$out = #{
in = Get-ChildItem "\\$server\C$\jobs\statements\input*\" -Recurse -File
out = Get-ChildItem "\\$server\C$\jobs\statements\output*\" -Recurse -File
}
# if $out['in'] and $out['out'] are `$null`, Ready is `$true`
[pscustomobject]#{
Ready = -not($out['in'] -or $out['out'])
Server = $server
Files = $out
}
}
Now, if you want to see which servers are Ready (no files in input and output):
$result.where{ $_.Ready }
And if you want to see which servers are not Ready, and have a list of the files:
$result.where{ -not $_.Ready }.foreach{
foreach($file in $_.Files.PSBase.Values.FullName) {
[pscustomobject]#{
Server = $_.Server
Files = $file
}
}
}

Need help writing ping results to separate files for up and down machines

I need to ping a list of machines and have the results split between up and down into two different .txt files.
$PingPCs = Get-Content "C:\Temp\Cache Cleanup Project\Computerlist.txt
foreach ($PC in $PingPCs) {
$up = Test-Connection $PC -Count 1 -Quiet
if($up) {
$Response = Test-Connection $PC -Count 1 | Select-Object Name
$Response ## Need help figuring out the outfile process to write to .txt ##
}
else {
$Failed = "$PC is not reachable"
$Failed ### Need help figuring out the outfile process to write to .txt ###
}
}
The two places I need help with is just writing the results to separate text files. One file named Online.txt and the other Offline.txt.
You can do something like this, instead of exporting the results on each iteration and appending to a file, it's a better idea to first perform your test and save the results in memory. Once all is complete, export the results:
$PingPCs = Get-Content "C:\Temp\Cache Cleanup Project\Computerlist.txt"
$result = foreach ($PC in $PingPCs)
{
$testConnection = Test-Connection $PC -Count 1 -Quiet
[pscustomobject]#{
ComputerName = $PC
TestConnection = $testConnection
}
}
$result.where({$_.TestConnection}) | Out-File "x:\path\to\online.txt"
$result.where({-not $_.TestConnection}) | Out-File "x:\path\to\offline.txt"
Edit
Adding nice optimization to export the results. Thanks #mklement0
$online, $offline = $result.where({$_.TestConnection},'Split')
$online | Out-File "x:\path\to\online.txt"
$offline | Out-File "x:\path\to\offline.txt"

Powershell Try catch write errors to an external txt file

I have a PowerShell script to disable email forwarding in Office 365 using a CSV file. I want to catch all errors to an external Txt file.
Here's my code:
$Groups |
ForEach-Object {
$PrimarySmtpAddress = $_.PrimarySmtpAddress
try {
# disable Mail forwarding
Set-Mailbox -Identity $PrimarySmtpAddress -ForwardingSmtpAddress $Null
}
Catch {
$PrimarySmtpAddress | Out-File $logfile -Append
}
}
but it doesn't catch errors.
Is there any instruction to catch errors to an external file?
Any solution will be helpful.
From Jeff Zeitlin's and TheMadTechnician's comments, changes noted in inline comments:
$Groups | ForEach-Object {
$PrimarySmtpAddress = $_.PrimarySmtpAddress
Try {
# disable Mail forwarding
#Added ErrorAction Stop to cause errors to be terminating
Set-Mailbox -Identity $PrimarySmtpAddress -ForwardingSmtpAddress $Null -ErrorAction Stop
}
Catch {
#Removed Write-Host as Write-Host writes to the host, not down the pipeline, Write-Output would also work
"$PrimarySmtpAddress" | Out-File $logfile -Append
}
}
Try this:
$ErrorMessage = $_.Exception.Message
#or $ErrorMessage= $Error[0].Exception.Message
if($ErrorMessage -ne $null) {
... Your custom code
}
Exchange Online (a.k.a O365) does not honor the thrown error for your catch statement to work. We have had to set
$global:ErrorActionPreference=Stop
to get the error object to return
Note: We implemented this using the following at beginning of function
$saved=$global:ErrorActionPreference
$global:ErrorActionPreference=Stop
and reverted the setting at the end of the function using
$global:ErrorActionPreference=$saved

Remote Powershell to retrieve specific registry value from lots of servers

I have the following..
$output = #()
$servers =Get-Content "C:\Windows\System32\List3.txt"
foreach ($server in $servers)
{
trap [Exception] {continue}
Import-Module PSRemoteRegistry
$key="SOFTWARE\Microsoft\'Microsoft Antimalware'\'Signature Updates'"
$regkey=Get-RegBinary -ComputerName $server -Key $Key -Value SignatuesLastUpdated
#$regkey=(Get-Item HKLM:\SOFTWARE\Microsoft\'Microsoft Antimalware'\'Signature Updates').getValue('SignaturesLastUpdated')
#$regkey=[datetime]::ParseExact("01/02/03", "dd/MM/yy", $null) | Export-csv -path c:\temp\avinfo.csv -append
#$regkey
}
$output | Select $server , $Regkey | Export-Csv c:\temp\avinfo.csv -NoTypeInformation
I think it's pretty close but doesn't work as needed - can anyone tell me what I am doing wrong here - been reading a lot and managed to get this far, just need the help to finalise.
Thanks
Ok... so there is alot that needed to be changed to get this to work. I will update the answer frequently after this is posted.
$servers = Get-Content "C:\Windows\System32\List3.txt"
$key="SOFTWARE\Microsoft\Microsoft Antimalware\Signature Updates"
$servers | ForEach-Object{
$server = $_
Try{
Get-RegBinary -ComputerName $server -Key $Key -Value SignatuesLastUpdated -ErrorAction Stop
} Catch [exception]{
[pscustomobject]#{
ComputerName = $server
Data = "Unable to retrieve data"
}
}
} | Select ComputerName,#{Label=$value;Expression={If(!($_.Data -is [string])){[System.Text.Encoding]::Ascii.GetBytes($_.data)}Else{$_.Data}}} | Export-Csv c:\temp\avinfo.csv -NoTypeInformation
What the above code will do is more in line with your intentions. Take the list and for each item get the key data from that server. If there is an issue getting that data then we output a custom object stating that so we can tell in the output if there was an issue. The part that is up in the air is how you want to export the binary data to file. As it stands it should create a space delimited string of the bytes.
The issues that you did have that should be highlighted are
No need to import the module for every server. Moved that call out of the loop
You have declared the variable $output but do not populate it during your loop process. This is important for the foreach construct. You were, in the end, sending and empty array to you csv. My answer does not need it as it just uses standard output.
As #Meatspace pointed out you had a typo here: SignatuesLastUpdated
Get-RegBinary does not by default create terminating errors which are needed by try/catch blocks. Added -ErrorAction Stop. Don't think your code trap [Exception] {continue} would have caught anything.
The single quotes you have in your $key might have prevented the path from being parsed. You were trying to escape spaces and just need to enclose the whole string in a set of quotes to achieve that.
While Select can use variables they are there, in a basic form, to select property names. In short what you had was wrong.

how to use out-file with a foreach loop

$web = Get-SPWeb http://mysite
ForEach($list in $web.Lists)
{
if($list.BaseType -eq "DocumentLibrary")
{
Write-Host $list.Fields
if($list.Fields.ContainsField("marking") -eq $true)
{
Write-Host "found" $list.Title
}
}
} | Out-File test.txt
I have this code, which doesn't work due to write-host outputting to command line so obviously it won't write to the file.
How can I make it so it doesn't output to the command line but just outputs all the items found to the text file
Place your Out-File with the -Append switch after the lines you want to write, and take out theWrite-Host.
This would be a cleaner way to do it and probably faster. You're not going to get the carriage
return you're getting in your script after $list.Fields but I suspect you don't want that anyway
$doclibraries = (Get-SPWeb http://mysite).Lists | where {$_.BaseType -eq 'DocumentLibrary'}
foreach ($library in $doclibraries) {
$line = $library.Fields
if ($Line.ContainsField('marking')) {
Add-Content -Value "$line found $($library.title)" -Path test.txt
}
}