Playing around with the Get-EventLog command. The first 3 commands work as expected.
Get-EventLog Application -Newest 1000 | Select Message
Get-EventLog System -Newest 1000 | Select Message
Get-EventLog Security -Newest 1000 | Select Message
But this does not work
Get-EventLog Setup -Newest 1000 | Select Message
and this does not work
Get-EventLog setup
How come? There are WSUS errors in the Setup that we'd like to capture.
Sorry this is a bit long but I'm kind of fond of try/catch statements and over-communication.
#requires -Version 2.0
function RemoteEventLog([Parameter(Mandatory=$true)]$LogName, $MaxEvents,[Parameter(Mandatory = $true)]$computers, $LogPath)
{
<#
.SYNOPSIS
Gather remote event logs by log name by computer names.
.DESCRIPTION
Specifiy a log name to narrow down the search. Provide as many computernames as needed.
.PARAMETER maxevents
-maxevents is all about how many events.
.PARAMETER computers
The array or list of comma separated computer names to run the script through.
.PARAMETER LogPath
-LogPath will let you decide the parent folder of the location to store the logs by computer name.
.EXAMPLE
RemoteEventLog -logname Application -maxevents 1000 -computers ('host1','host2','host3')
This will loop through the computers and bring back the log for each computer.
#>
$computers = $computers -split (',')
try
{
$testLogPath = Test-Path $LogPath
}
catch
{
"Error was $_"
$line0 = $_.InvocationInfo.ScriptLineNumber
"Error was in Line $line0"
}
if(!($testLogPath))
{
try
{
New-Item -Path $LogPath -ItemType Directory -ErrorAction:Stop
}
catch
{
"Error was $_"
$line1 = $_.InvocationInfo.ScriptLineNumber
"Error was in Line $line1"
}
}
foreach($computer in $computers)
{
try
{
$log = Get-WinEvent -LogName $logName -MaxEvents $maxevents -ComputerName $computer -ErrorAction:Stop
}
catch
{
"Error was $_"
$line2 = $_.InvocationInfo.ScriptLineNumber
"Error was in Line $line2"
}
try
{
New-Item -Path $LogPath -Name ("$computer.evt") -Value $log -Force
$log | Out-File -FilePath $LogPath
}
catch
{
"Error was $_"
$line3 = $_.InvocationInfo.ScriptLineNumber
('Error was in Line {0}' -f $line3)
}
}
}
RemoteEventLog -logname Application -MaxEvents 100 -computers 'localhost,computer2,computer3' -LogPath D:\Desktop\logs
Related
I'm very new to PowerShell and my end goal is to backup event logs on remote servers to a fileshare on the network. I was able to get my script working locally on a single server, backing up the servers event logs to a folder. Now, I'm trying to run this script from "server A" and backup event logs on "Server B" and "Server C". I was hoping I can accomplish this via simply creating a new ps session for each server and copy pasting my code. This of course wasn't the case. I'm guessing I might have to go through and add -Computer parameters to somethings?
I got the foundational code from this website
Was hoping a PS guru could point out my failures, here's my code:
$Servers = 'ServerB'
$ArchiveServer = 'ServerA' #For Testing
foreach ($Server in $Servers){
Enter-PSSession -ComputerName $Server -Credential mycred
$RemoteArchive = "\\" + $ArchiveServer +"\c$\z-TestScript-lastname"
$LocalArchive = "C:\z-TestScript"
$ArchiveComp = "\$Server"
$ArchiveFolder = "\Archive-" + (Get-Date -Format "yyyy-MM-dd") + "test14" ### Added test to get past testpath check
$RemoteLogFolder = "\\" + $Server + "\c$\Windows\System32\winevt\Logs"
$LogFolder = "C:\Windows\System32\winevt\Logs\*"
$RemoteCompressedPath = -join("$RemoteArchive","$ArchiveComp","$ArchiveFolder",".zip")
$CompressedPath = -join("$ArchiveLoc","$ArchiveComp","$ArchiveFolder",".zip")
$RemoteArchivePath = -join("$RemoteArchive","$ArchiveComp","$ArchiveFolder")
$ArchivePath = -join("$ArchiveLoc","$ArchiveComp","$ArchiveFolder")
$winlogs = 'application', 'security', 'system', 'setup', 'hardwareevents', 'internet explorer', 'key management service', 'oalerts', 'Parameters', 'oneapp_IGCC', 'windows powershell'
# Checking paths
If (!(Test-Path $RemoteArchive)) {
Write-Host
Write-Host "Archive folder $RemoteArchive does not exist, aborting ..." -ForegroundColor Red
Exit
}
If ((Test-Path $RemoteArchivePath)) {
Write-Host
Write-Host "Archive path $RemoteArchivePath exists, aborting ..." -ForegroundColor Red
Exit
}
If (!(Test-Path $RemoteArchivePath)) {
Write-Host
Write-Host "Creating Archive folder $RemoteArchivePath ..." -ForegroundColor Red
New-Item -Path $RemoteArchivePath -type directory -Force
}
# For all newer event logs, archive.
foreach ($winlog in $winlogs) {
# Configure environment
$sysName = $Server
$eventName = "$winlog Event Log Monitoring"
# Add event source to log if necessary
If (-NOT ([System.Diagnostics.EventLog]::SourceExists($eventName))) {
New-EventLog -ComputerName $Server -LogName $winlog -Source $eventName
}
# Check the log
if ( $winlog -ne 'Setup'){
$Log = Get-WmiObject Win32_NTEventLogFile | Where-Object {$_.logfilename -eq "$winlog"}
# Archive the log
$ArchiveFile = $ArchivePath + "\$winlog-" + (Get-Date -Format "yyyy-MM-dd#HHmm") + ".evt"
$EventMessage = "The $winlog event log will now be backed up."
$Results = ($Log.BackupEventlog($ArchiveFile)).ReturnValue
If ($Results -eq 0) {
# Successful backup of the event log
$Results = ($Log.ClearEventlog()).ReturnValue
$EventMessage += "The $winlog event log was successfully archived to $ArchiveFile and cleared."
Write-Host $EventMessage
Write-EventLog -LogName $winlog -Source $eventName -EventId 11 -EntryType Information -Message $eventMessage -Category 0
}
Else {
$EventMessage += "The #winlog event log could not be archived to $ArchiveFile and was not cleared. Review and resolve security event log issues on $sysName ASAP!"
Write-Host $EventMessage
Write-EventLog -LogName $winlog -Source $eventName -EventId 11 -EntryType Error -Message $eventMessage -Category 0
}
# Close the log
$Log.Dispose()
}
# For older archives like setup, archive.
Else {
$Log = Get-winevent -Listlog Setup | select Logname, Logfilepath | ForEach-Object -Process {
$name = $_.Logname
$safename = $name.Replace("/","-")
$logpath = $_.Logfilepath
$path = $ArchivePath + "\Setup" + (Get-Date -Format "yyyy-MM-dd#HHmm") + ".evt"
wevtutil.exe EPL $safename $path
Write-Host "Copying Setup log to: $path"
}
}
}
#Copy any windows archived logs over, then delete
Write-Host "Copying Archived Logs..."
Get-ChildItem -Path $LogFolder -Include Archive* -Recurse | Copy-Item -Destination $ArchivePath
Get-ChildItem -Path $LogFolder -Include Archive* -Recurse | Remove-Item
#Compress the folder
Write-Host "Compressing $ArchivePath and moving to $CompressedPath"
Compress-Archive -Path $ArchivePath -Destination $CompressedPath
Exit-PSSession
}
Main errors I'm getting as it goes through the foreach loop for the winlogs, it seems to run it twice, once for remote pc and once for local.
I have written a small function that writes into a single logfile for each server this module is installed, but somehow a few cycles to write the line are lost, sometime I get the error file already in use. I have tested this function on different version of Powershell, PS5.1, PS6, PS7.1 x86 and x64 all with the same result.
if (!(Test-Path -ErrorAction Stop -Path (Join-path -Path $ScriptLogPath -ChildPath "$FQDN.log"))) {
New-Item -Path $ScriptLogPath -Name "$FQDN.log" -Force -ItemType File -ErrorAction Stop
}
$CreatedNew = $false
$Global:MTX = New-Object -TypeName System.Threading.Mutex($true, 'Global\LogFileMutex', [ref]$CreatedNew)
try {
if (-not $CreatedNew) {
$Global:MTX.WaitOne(10000) | Out-Null
}
}
catch [System.Threading.AbandonedMutexException] {
if (!(Test-Path -Path 'HKLM:\System\CurrentControlSet\services\eventlog\Application\LogWriter')) {
New-EventLog -LogName Application -Source LogWriter
}
Write-EventLog -LogName "Application" -Source "LogWriter" -EntryType Error -Message $_.Exception -EventId 1000
}
($Date + $InstanceId + $Severity + $ScriptLineNumber + $ScriptName + $Message) | Out-File -Append -FilePath (Join-path -Path $ScriptLogPath -ChildPath "$FQDN.log")
}
catch {
if (!(Test-Path -Path 'HKLM:\System\CurrentControlSet\services\eventlog\Application\LogWriter')) {
New-EventLog -LogName Application -Source LogWriter
}
Write-EventLog -LogName "Application" -Source "LogWriter" -EntryType Error -Message $_.Exception -EventId 1000
}
finally {
if ($Null -ne $Global:MTX) {
[void]$Global:MTX.ReleaseMutex()
[void]$Global:MTX.Dispose()
}
}
That is the block of code that will append the formatted string into the file.
So fare I have tried different approaches to handle the Mutex, made them globally and not. Used different approaches to append the string to the file, like Add-Content.
To try out the function I use this little script, it should write 150 lines, but end up with about 130 and no single error.
for ($num1 = 1 ; $num1 -le 5 ; $num1++) {
Start-Job {
for ($num = 1 ; $num -le 10 ; $num++) {
Write-Log -Severity 'INFO' -Message "Starting Job $num"
Start-Job -ScriptBlock {
try {
Start-Sleep -Milliseconds (Get-Random -Maximum 10)
Write-Log -Severity 'INFO' -Message "Starting"
Start-Sleep -Milliseconds (Get-Random -Maximum 10)
Write-Log -Severity 'INFO' -Message "DONE"
}
catch {
New-Item -Path "C:\Users\User\Desktop" -Name (Get-Date -Format "dd/MM/yyyy HH:mm:ss") -ItemType File
}
}
}
}
Since every server is writing to the same logfile, i dont think that you can override "error file already in use"
Iam thinking that maby you should think over the concept and run from a server where you are monitoring by invoke-commmand ?
I have been trying to make a powershell script to detect what antivirus software is installed, and then uninstall it.
I have been able to detect what antivirus is installed using WMI.
I cant find a way to uninstall antivirus software via powershell however.
Is there a way to do this?
Hope you guys can help.
The script i use to detect antivirus:
function Get-AntivirusName {
[cmdletBinding()]
param (
[string]$ComputerName = "$env:computername" ,
$Credential
)
BEGIN
{
$wmiQuery = "SELECT * FROM AntiVirusProduct"
}
PROCESS
{
$AntivirusProduct = Get-WmiObject -Namespace "root\SecurityCenter2" -Query $wmiQuery #psboundparameters
[array]$AntivirusNames = $AntivirusProduct.displayName
Switch($AntivirusNames) {
{$AntivirusNames.Count -eq 0}{"No Antivirus installed";Continue}
{$AntivirusNames.Count -eq 1 -and $_ -eq "Windows Defender"} {"Only Windows Defender is installed!";Continue}
{$_ -ne "Windows Defender"} {"Antivirus installed ($_)."}
}
}
END {
}
}
$av = Get-AntivirusName
Add-Type -AssemblyName PresentationFramework
[System.Windows.MessageBox]::Show($av,'Antivirus')
You could try the following, from https://community.spiceworks.com/scripts/show/3161-detect-and-remove-software-powershell:
################################################
# Powershell Detect and Remove software script #
# #
# V1.0 - Gav #
################################################
# - Edit the Variables below and launch the #
# script as an account that can access the #
# machines. #
# - Script will check that logs exist and #
# create them if needed. #
################################################
cls
#VARIABLES - EDIT BELOW
$software = "INSERT SOFTWARE HERE" # - Enter the name as it appears from WMIC query. WMIC PRODUCT NAME
$textfile = "C:\path\pclist.txt"
$Successlogfile = "C:\path\Done_Machines.txt"
$Errorlogfile = "C:\path\Failed_Machines.txt"
#Date Calculation for Logs
$today = Get-Date
$today = $today.ToString("dddd (dd-MMMM-yyyy)")
#Load PC's From Text File
$computers = Get-Content "$textfile"
#Check if Log Files Exist
If (Test-Path $Successlogfile) {
Write-Host -ForegroundColor Green "Success Log File Exists, Results will be appended"
}
else
{
Write-Host -ForegroundColor Red "Success Log File does not exist, creating log file"
New-Item -path $Successlogfile -ItemType file
}
If (Test-Path $Errorlogfile) {
Write-Host -ForegroundColor Green "Error Log File Exists, Results will be appended"
}
else
{
Write-Host -ForegroundColor Red "Error Log File does not exist, creating log file"
New-Item -path $Successlogfile -ItemType file
}
#Run Ping Test and Uninstall if turned on
foreach ($computer in $computers) {
If (Test-Connection -quiet -ErrorAction SilentlyContinue -computername $computer -count 2)
{
Write-Host -ForegroundColor Green "$Computer is responding, Attempting Uninstall of $Software"
Add-Content $Successlogfile "`n$today`n$Computer`n"
Get-WmiObject -class Win32_Product -ComputerName $computer | Where-Object {$_.Name -match $software} | ForEach-Object { $_.Uninstall()}
}
else
{
Write-Host -ForegroundColor Red "$Computer is not responding"
Add-Content $Errorlogfile "`n$today`n$Computer`n"
}
}
Hi all powershell gurus out there.
I have a foreach which opens a URL from URLListl.txt and doing a Measure-command on them. The result from Measure-command writes to event-log.
In another text file i have country list, like:
USA
Canada
Brazil
and so on....
I want in write eventlogs Message to read from this Text file and write it with the Measure-command result in Message part of the eventlog.
The below is my script which works fine but it creates two entries for each URL.
{
$URLListFile = "C:\yourname\URLList.txt"
$URLList = Get-Content $URLListFile -ErrorAction SilentlyContinue
Foreach ($Uri in $URLList)
{
$Time = Measure-Command {
C:\yourname\MainScript.ps1}
$Time.TotalSeconds
$countrycode = (Get-Content c:\yourname\URLListcountry.txt)
foreach ($SiteCode in $CountryCode)
{
$LogFileExist = Get-EventLog -list | Where-Object { $_.LogDisplayName -eq "Scriptcheck" }
if (! $LogFileExist)
{
New-EventLog -LogName "Scriptcheck" -Source "Scripts"
}
if ($Time.Totalseconds -lt 25)
{
Write-EventLog -LogName "Scriptcheck" -Source "Scripts" -EntryType information -EventId 100 -Message " $SiteCode `nTotal Time: $Time"
}
elseif ($Time.Totalseconds -gt 25)
{
Write-EventLog -LogName "Scriptcheck" -Source "Scripts" -EntryType warning -EventId 101 -Message " $SiteCode `nTotal Time: $Time"
}
}
}
}
if (Get-Process -name iexplore -ErrorAction SilentlyContinue)
{
Stop-Process -Name iexplore
}
Measure-Site
So say you want to look at the last 3 thing in the eventlog for type application, from all the desktops on the lan. my problem is, the $a below makes an array (i think), and i want to write that to a file. right now it "works" but just spits out a few blank lines per machine. if $msg = is commented out, i get this "Exception calling "Join" with "2" argument(s): "Value cannot be null."
$servers = "machine1, "machine2"
$date = ( get-date ).ToString('yyyyMMdd')
$file = New-Item -type file "c:\temp\$date-test1.txt" -Force
$msg = ""
foreach($server in $servers){
Write-Host "Connect to $server..."
add-content $file "$server $date"
$a = Get-EventLog -Logname application -ComputerName $server -EntryType warning -newest 3 |
Format-Table -Wrap -Property Source,Message -Autosize
foreach($element in $a)
{[string]::join($element, $msg)}
Write-Host $msg
#add-content $file $msg
}
You are thinking about text, not objects in the pipeline. Don't try to parse, join or mess around with strings.
$servers = "machine1, "machine2"
$date = ( get-date ).ToString('yyyyMMdd')
$file = New-Item -type file "c:\temp\$date-test1.txt" -Force
Get-EventLog -log Application -Computer $servers -entry Warning -newest 3 | Select MachineName,Source,Message | out-file $file