Using Powershell to backup remote computers event logs - powershell

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.

Related

Deactivating all "ready/running Tasks" on a bunch of servers in Specific Folders with the ability to activate those tasks again

i am looking for a Powershell script that can disable all tasks that are "ready" or "running" in a specific folder in the Task Scheduler on 3 or more servers.
After we updated the software i should be able to activate all the tasks that were disabled by the script again, not just activate all disabled scripts, but specificly the ones that were disabled by the script.
I know this should be possible, but i am not capable of assembling the single parts. Everything thats more than a single command is to much for my logic capacitys.
<#getScheduledTasksinfo-FreeholdReboots.ps1
.Synopsis
PowerShell script to list all Scheduled Tasks, the User ID and the State
.DESCRIPTION
This script scans the content of the c:\Windows\System32\tasks and searches the UserID XML value.
The output of the script is a comma-separated log file containing the Computername, Task name, UserID, Status.
.Author
UNKNOWN
.Tweaker
Patrick Burwell
#>
Remove-Item -Force "D:\batch\Logs\Maintenance\$day-SchedTasks-Reboots.csv"
$logfilepath = "D:\batch\Logs\Maintenance\$day-SchedTasks-Reboots.csv"
$ErrorActionPreference = "SilentlyContinue"
$serverlist = gc D:\batch\input\servers.txt
foreach($server in $serverlist){
if($server -like '#*')
{
continue
}
Write-Host $server
$path = "\\" + $server + "\c$\Windows\System32\Tasks"
$tasks = Get-ChildItem -Path $path -File
if ($tasks)
{
Write-Verbose -Message "I found $($tasks.count) tasks for $server"
}
foreach ($item in $tasks)
{
if($item -like 'Optimize Start Menu*'){continue}
if ($item -like "User_Feed_Synchronization*"){continue}
if($item -like 'ComputeSensorWatchDog*'){continue}
if ($item -like "Google*"){continue}
$AbsolutePath = $path + "\" + $item.Name
$task = [xml] (Get-Content $AbsolutePath)
$states = (Get-ScheduledTask -Verbose -TaskPath '\' -TaskName "reboot")
[STRING]$check = $task.Task.Principals.Principal.UserId
[STRING]$state = $states.State
if ($task.Task.Principals.Principal.UserId)
{
if($item -ilike "*reboot*"){
Write-Verbose -Message "Writing the log file with values for $server"
Add-content -path $logfilepath -Value "$server,$item,$check,$state"
}
else {continue}
}
}
}

Powershell: Write to a single Logfile

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 ?

Detect and uninstall antivirus

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"
}
}

Powershell Workflow: Using if/else statement?

I am trying to use a workflow in order to make a script run faster by running a ping on several machines in parallel. Where I am getting stuck is trying to use my if/else statement within the workflow and adding log entries based on the results of the ping. Right now, it is not writing anything to the logs based on the ping results. Any assistance would be greatly appreciated. I am open to suggestions for different methods to do this, as long as they do not involve using a module. I want to stick with just Powershell code (I do not have permissions to install modules on the server where this would be running). Thank you! Here is my current code:
<#
.NOTES
===========================================================================
Created on: 10/6/2016 8:06 AM
Created by:
Organization:
Filename: Get-MPOSOfflinePrinters.ps1
===========================================================================
.DESCRIPTION
#>
$logfile = "D:\Logs\MPOSPrinterPingLog.txt"
$offlineprinters = "D:\Reports\MPOS\MPOSOfflinePrinters.txt"
If (Test-Path $logfile) {Remove-Item $logfile}
If (Test-Path $offlineprinters) {Remove-Item $offlineprinters}
Add-Content $logfile "Setting local path"
$localPath = "C:\Temp\MPOS"
Add-Content $logfile "Gathering server list"
$serverList = (Get-ADComputer -Filter "Name -like 'Q0*P30' -or Name -like 'Q0*P32'" -SearchBase "OU=Print,OU=Prod,OU=POS,DC=COMPANY,DC=NET").name | Sort-Object | Out-File C:\Temp\MPOS\MPOSPrintServers.txt
$serverListPath = "C:\Temp\MPOS\MPOSPrintServers.txt"
#Retrieve a list of MPOS Print servers from text file and set to $serverNames
Add-Content $logfile "Compiling text file"
$serverNames = Get-Content -Path $serverListPath
#Iterate through each of the server names
foreach ($serverName in $serverNames) {
Add-Content $logfile "Processing $serverName"
#Check if the server is online before doing the remote command
If (Test-Connection -ComputerName $serverName -Quiet -count 1) {
Add-Content $logfile "$serverName is online"
#copy config file from MPOS print to local server for processing
$timestamp1 = (Get-Date -Format g)
Add-Content $logfile "$timestamp1 - Copying xml file from server to local path"
$configPath = "\\$($serverName)\C$\ProgramData\Microsoft\Point Of Service\Configuration\Configuration.xml"
Copy-Item $configPath $localPath
#process xml file to parse IP addresses for ping
$timestamp2 = (Get-Date -Format g)
Add-Content $logfile "$timestamp2 - Processing xml file from $serverName to parse data to csv"
$xml = [xml](Get-Content C:\Temp\MPOS\Configuration.xml)
$PrinterNames = $xml.selectNodes('//PointOfServiceConfig/ServiceObject/Device') | foreach {New-Object -TypeName psobject -Property #{LogicalName=$_.LogicalName.Name}}
$PrinterIPs = $xml.selectNodes('//PointOfServiceConfig/ServiceObject/Device') | foreach {New-Object -TypeName psobject -Property #{HardwarePath=$_.HardwarePath}}
workflow Test-WFConnection {
param(
[string[]]$PrinterIPs
)
foreach -parallel ($PrinterIP in $PrinterIPs) {
$pingableIP = $PrinterIP.HardwarePath
If (Test-Connection -ComputerName $pingableIP -Quiet -Count 1 -ErrorAction SilentlyContinue) {
$timestamp3 = (Get-Date -Format g)
Add-Content -Path $logfile -Value ("$timestamp3 - $serverName, $pingableIP is online and pingable")
} #If (Test-Connection $pingableIP -Quiet -Count 1 -ErrorAction SilentlyContinue) {
Else {
$timestamp3 = (Get-Date -Format g)
Add-Content -Path $offlineprinters -Value ("$timestamp3 - $serverName, $pingableIP is offline!")
} #Else
} #foreach -parallel ($PrinterIP in $PrinterIPs) {
} #workflow Test-WFConnection {
Test-WFConnection -PSComputerName $PrinterIPs
} #If (Test-Connection -ComputerName $serverName -Quiet -count 1) {
Else {
Add-Content $logfile "$serverName is offline!"
} #Else
} #foreach ($serverName in $serverNames) {

Find old computers in AD is not accurate?

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