Powershell Get-EventLog from computers.txt and save data - powershell

I have some problems getting EventLog and save data. I am able to get my EventLogs but not logs from network computers.
Here is the code I am running:
$logFileName = "Application"
$path = $MyInvocation.MyCommand.Path +"\Output\"
$path = $PSScriptRoot+"\Output\"
new-item $path -ItemType directory
$array = ("System", "Security")
$file = $PSScriptRoot +"\computers.txt"
$users = ForEach ($machine in $(Get-Content $file)) {
$pathMachine = $path+$machine
new-item $pathMachine -ItemType directory
ForEach ($logFileName in $array){
# do not edit
$logFileName
$exportFileName = (get-date -f yyyyMMdd) + "_" + $logFileName + ".evt"
$logFile = Get-WmiObject Win32_NTEventlogFile -ComputerName $machine | Where-Object {$_.logfilename -eq $logFileName}
$logFile
$exportFileName
$pathMachine
$temp = $pathMachine + "\"+ $exportFileName
$temp
$fff = $logFile.BackupEventLog($temp)
}
}

This could e considered a duplicate of this.
Reading event log remotely with Get-EventLog in Powershell
# swapped from this command
get-eventlog -LogName System -computername <ServerName>
# to this
invoke-command {get-eventlog -LogName System} -ComputerName <ServerName>
Don't struggle with writing this from scratch. Well, unless it's a learning exercise. There are pre-built script for you to leverage as is and or tweak as needed.
Running commands on Remote host require using the Invoke cmdlet, and or an established PSRemoting session to that host.
Get Remote Event Logs With Powershell
Gather the remote event log information for one or more systems using wmi, alternate credentials, and multiple runspaces. Function supports custom timeout parameters in case of wmi problems and returns Event Log information for the specified number of past hours.
Download: Get-RemoteEventLogs.ps1
The script is too long (it's 100+ lines) to post here, but here in the Synopsis of it.
Function Get-RemoteEventLogs
{
<#
.SYNOPSIS
Retrieves event logs via WMI in multiple runspaces.
.DESCRIPTION
Retrieves event logs via WMI and, if needed, alternate credentials. This function utilizes multiple runspaces.
.PARAMETER ComputerName
Specifies the target computer or comptuers for data query.
.PARAMETER Hours
Gather event logs from the last number of hourse specified here.
.PARAMETER ThrottleLimit
Specifies the maximum number of systems to inventory simultaneously
.PARAMETER Timeout
Specifies the maximum time in second command can run in background before terminating this thread.
.PARAMETER ShowProgress
Show progress bar information
.EXAMPLE
PS > (Get-RemoteEventLogs).EventLogs
Description
-----------
Lists all of the event logs found on the localhost in the last 24 hours.
.NOTES
Author: Zachary Loeber
Site: http://www.the-little-things.net/
Requires: Powershell 2.0
Version History
1.0.0 - 08/28/2013
- Initial release
#>
Or this one.
PowerShell To Get Event Log of local or Remote Computers in .csv file
This script is handy when you want to extract the eventlog from remote or local machine. It has multiple filters which will help to filter the data. You can filter by logname,event type, source etc. This also have facility to get the data based on date range. You can change th
Download : eventLogFromRemoteSystem.ps1
Again, too big to post here because the length is like the other one.

I am working on some assumptions but maybe this will help.
When I Ran your Code I got
Get-Content : Cannot find path 'C:\computers.txt' because it does not exist.
I had to make the C:\computers.txt file, then I ran your code again and got this error.
Get-Content : Cannot find path 'C:\Output\computers.txt' because it does not exist.
I made that file in that location, then I ran your code again and I got the event log file. Maybe try creating these two missing files with a command like
Get-WmiObject Win32_NTEventlogFile -ComputerName $machine
mkdir C:\Output\$machine
$env:computername | Out-File -FilePath c:\Output\Computers.txt
You may also want to setup a Network share and output to that location so you can access the event logs from a single computer. Once the share is setup and the permissions just drop the unc path in.

Related

How to shutdown the computer after closing the powershell window?

I am new to powershell. I have a powershell script I've been using to backup my files. After it runs, I would like to shutdown the computer and close the powershell window. It seems I can do one or the other, but not both. So when I restart the computer, powershell complains that it was not closed properly.
How to shutdown the computer after closing the powershell window?
TIA
p.s. Contrary to popular belief, I have read the manual. However, as mentioned below, if I put EXIT before Stop-Computer, the script exits before executing Stop-Computer. If I put EXIT after Stop-Computer, powershell complains that the file was not closed properly on reboot. Either way, I lose. :(
PowerShell does provid and 'Exit', as noted in my comment. As for stopping, just put the 'Stop-Computer' cmdlet at the end of your script to shut down the computer.
Get-Help -Name Stop-Computer -examples
# Results
<#
NAME
Stop-Computer
SYNOPSIS
Stops (shuts down) local and remote computers.
----------- Example 1: Shut down the local computer -----------
Stop-Computer -ComputerName localhost
Example 2: Shut down two remote computers and the local computer
Stop-Computer -ComputerName "Server01", "Server02", "localhost"
`Stop-Computer` uses the ComputerName parameter to specify two remote computers and the local computer. Each computer is shut down.
-- Example 3: Shut down remote computers as a background job --
$j = Stop-Computer -ComputerName "Server01", "Server02" -AsJob
$results = $j | Receive-Job
$results
`Stop-Computer` uses the ComputerName parameter to specify two remote computers. The AsJob parameter runs the command as a background job. The job objects are stored in the `$j` variable.
The job objects in the `$j` variable are sent down the pipeline to `Receive-Job`, which gets the job results. The objects are stored in the `$results` variable. The `$results` variable displays the job information
in the PowerShell console.
Because AsJob creates the job on the local computer and automatically returns the results to the local computer, you can run `Receive-Job` as a local command.
------------ Example 4: Shut down a remote computer ------------
Stop-Computer -ComputerName "Server01" -Impersonation Anonymous -DcomAuthentication PacketIntegrity
`Stop-Computer` uses the ComputerName parameter to specify the remote computer. The Impersonation parameter specifies a customized impersonation and the DcomAuthentication parameter specifies authentication-level
settings.
---------- Example 5: Shut down computers in a domain ----------
$s = Get-Content -Path ./Domain01.txt
$c = Get-Credential -Credential Domain01\Admin01
Stop-Computer -ComputerName $s -Force -ThrottleLimit 10 -Credential $c
`Get-Content` uses the Path parameter to get a file in the current directory with the list of domain computers. The objects are stored in the `$s` variable.
`Get-Credential` uses the Credential parameter to specify the credentials of a domain administrator. The credentials are stored in the `$c` variable.
`Stop-Computer` shuts down the computers specified with the ComputerName parameter's list of computers in the `$s` variable. The Force parameter forces an immediate shutdown. The ThrottleLimit parameter limits the
command to 10 concurrent connections. The Credential parameter submits the credentials saved in the `$c` variable.
#>
Or use the Restart-Computer cmdlet, if that is your goal instead.
Update
Use two scripts, main and child.
# Start-Main.ps1
0..4 |
ForEach{
"Inside function... $PSItem"
Start-Sleep -Seconds 1
}
.\Start-Child
Exit
# Start-Child.ps1
'Preparing to shutdown in 10 seconds'
Start-Sleep -Seconds 10
Stop-Computer
or Using PS Jobs is another option as noted in my comment:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/start-job?view=powershell-7.2

How to prevent multiple instances of the same PowerShell 7 script?

Context
On a build server, a PowerShell 7 script script.ps1 will be started and will be running in the background in the remote computer.
What I want
A safenet to ensure that at most 1 instance of the script.ps1 script is running at once on the build server or remote computer, at all times.
What I tried:
I tried meddling with PowerShell 7 background jobs (by executing the script.ps1 as a job inside a wrapper script wrapper.ps1), however that didn't solve the problem as jobs do not carry over (and can't be accessed) in other PowerShell sessions.
What I tried looks like this:
# inside wrapper.ps1
$running_jobs = $(Get-Job -State Running) | Where-Object {$_.Name -eq "ImportantJob"}
if ($running_jobs.count -eq 0) {
Start-Job .\script.ps1 -Name "ImportantJob" -ArgumentList #($some_variables)
} else {
Write-Warning "Could not start new job; Existing job detected must be terminated beforehand."
}
To reiterate, the problem with that is that $running_jobs only returns the jobs running in the current session, so this code only limits one job per session, allowing for multiple instances to be ran if multiple sessions were mistakenly opened.
What I also tried:
I tried to look into Get-CimInstance:
$processes = Get-CimInstance -ClassName Win32_Process | Where-Object {$_.Name -eq "pwsh.exe"}
While this does return the current running PowerShell instances, these elements carry no information on the script that is being executed, as shown after I run:
foreach ($p in $processes) {
$p | Format-List *
}
I'm therefore lost and I feel like I'm missing something.
I appreciate any help or suggestions.
I like to define a config path in the $env:ProgramData location using a CompanyName\ProjectName scheme so I can put "per system" configuration.
You could use a similar scheme with a defined location to store a lock file created when the script run and deleted at the end of it (as suggested already within the comments).
Then, it is up to you to add additional checks if needed (What happen if the script exit prematurely while the lock is still present ?)
Example
# Define default path (Not user specific)
$ConfigLocation = "$Env:ProgramData\CompanyName\ProjectName"
# Create path if it does not exist
New-Item -ItemType Directory -Path $ConfigLocation -EA 0 | Out-Null
$LockFilePath = "$ConfigLocation\Instance.Lock"
$Locked = $null -ne (Get-Item -Path $LockFilePath -EA 0)
if ($Locked) {Exit}
# Lock
New-Item -Path $LockFilePath
# Do stuff
# Remove lock
Remove-Item -Path $LockFilePath
Alternatively, on Windows, you could also use a scheduled task without a schedule and with the setting "If the task is already running, then the following rule applies: Do not start a new instance". From there, instead of calling the original script, you call a proxy script that just launch the scheduled task.

using invoke-command to create files on a remote server

I am new to powershell and all sorts of scripting and have been landed with the following task.
I need to create a file on a remote server based on the filename picked up on the local server using the invoke-command.
WinRM is configured and running on the remote server.
What i need to happen is the following
On Server1 a trigger file is placed folder. Powershell on Server1 passes the filename onto powershell on Server2. Powershell on Server2 then creates a file based on the name.
My heads been melted trolling through forms looking for inspiration, any help would be greatly appreciated
Many thanks
Paul
I think if you're new to scripting, something that will add a lot of extra complexity is storing and handling credentials for Invoke-Command. It would be easier if you could make a shared folder on Server2 and just have one PowerShell script writing to that.
Either way, a fairly simple approach is a scheduled task on Server1 which runs a PowerShell script, with its own service user account, every 5 minutes.
Script does something like:
# Check the folder where the trigger file is
# assumes there will only ever be 1 file there, or nothing there.
$triggerFile = Get-ChildItem -LiteralPath "c:\triggerfile\folder\path"
# if there was something found
if ($triggerFile)
{
# do whatever your calculation is for the new filename "based on"
# the trigger filename, and store the result. Here, just cutting
# off the first character as an example.
$newFileName = $triggerFile.Name.Substring(1)
# if you can avoid Invoke-Command, directly make the new file on Server2
New-Item -ItemType File -Path '\\server2\share\' -Name $newFileName
# end here
# if you can't avoid Invoke-Command, you need to have
# pre-saved credentials, e.g. https://www.jaapbrasser.com/quickly-and-securely-storing-your-credentials-powershell/
$Credential = Import-CliXml -LiteralPath "${env:\userprofile}\server2-creds.xml"
# and you need a script to run on Server2 to make the file
# and it needs to reference the new filename from *this* side ("$using:")
$scriptBlock = {
New-Item -ItemType File -Path 'c:\destination' -Name $using:newFileName
}
# and then invoke the scriptblock on server2 with the credentials
Invoke-Command -Computername 'Server2' -Credential $Credential $scriptBlock
# end here
# either way, remove the original trigger file afterwards, ready for next run
Remove-Item -LiteralPath $triggerFile -Force
}
(Untested)

Check AD users' network share mapping with PowerShell

I tried using net use, net share, among others - none of which return the expected output. So instead, I'm modifying a script I found to see which network drives/shares are mapped to a user the script is pushed to. Then I go to my log file, look at the data, and determine if the account is set up properly. Here's the current script:
Get-WmiObject Win32_MappedLogicalDisk -computer $env:COMPUTERNAME | select name, providername
Out-File -filepath "\\*UNC filepath*\Mapped_Drives_$env:USERNAME$(get-date -Format _MM-dd-yy" # "HH.mm.ss" "tt).txt"
When I run it, the log file returns empty and I'm not sure why. I changed "Out-File -filepath" to "Start-Transcript" which isn't working the way I want it to either (with too much verbose output). It outputs fine in my PowerShell ISE with all the proper shares listed, but doesn't work when I navigate to the logged output. What am I missing?
You must pipe the output into the logfile
$logfile = "\\*UNC filepath*\Mapped_Drives_$env:USERNAME$(get-date -Format _MM-dd-yy" # "HH.mm.ss" "tt).txt"
Get-WmiObject Win32_MappedLogicalDisk | select name, providername | Out-File $logfile
On a more general note I'd use the commands to fix the mapped drives right there and then, instead of just writing them to a logfile for later inspection.

Powershell - Copying File to Remote Host and Executing Install exe using WMI

EDITED: Here is my code now. The install file does copy to the remote host. However, the WMI portion does not install the .exe file, and no errors are returned. Perhaps this is a syntax error with WMI? Is there a way to just run the installer silently with PsExec? Thanks again for all the help sorry for the confusion:
#declare params
param (
[string]$finalCountdownPath = "",
[string]$slashes = "\\",
[string]$pathOnRemoteHost = "c:\temp\",
[string]$targetJavaComputer = "",
[string]$compname = "",
[string]$tempPathTarget = "\C$\temp\"
)
# user enters target host/computer
$targetJavaComputer = Read-Host "Enter the name of the computer on which you wish to install Java:"
[string]$compname = $slashes + $targetJavaComputer
[string]$finalCountdownPath = $compname + $tempPathTarget
#[string]$tempPathTarget2 =
#[string]$finalCountdownPath2 = $compname + $
# say copy install media to remote host
echo "Copying install file and running installer silently please wait..."
# create temp dir if does not exist, if exist copy install media
# if does not exist create dir, copy dummy file, copy install media
# either case will execute install of .exe via WMII
#[string]$finalCountdownPath = $compname + $tempPathTarget;
if ((Test-Path -Path $finalCountdownPath) )
{
copy c:\hdatools\java\jre-7u60-windows-i586.exe $finalCountdownPath
([WMICLASS]"\\$targetJavaComputer\ROOT\CIMV2:win32_process").Create("cmd.exe /c c:\temp\java\jre-7u60-windows-i586.exe /s /v`" /qn")
}
else {
New-Item -Path $finalCountdownPath -type directory -Force
copy c:\hdatools\dummy.txt $finalCountdownPath
copy "c:\hdatools\java\jre-7u60-windows-i586.exe" $finalCountdownPath
([WMICLASS]"\\$targetJavaComputer\ROOT\CIMV2:win32_process").Create("cmd.exe /c c:\temp\java\jre-7u60-windows-i586.exe /s /v`" /qn")
}
I was trying to get $Job = Invoke-Command -Session $Session -Scriptblock $Script to allow me to copy files on a different server, because I needed to off load it from the server it was running from. I was using the PowerShell Copy-Item to do it. But the running PowerShell script waits until the file is done copying to return.
I want it to take as little resources as possible on the server that the powershell is running to spawn off the process on another server to copy the file. I tried to user various other schemes out there, but they didn't work or the way I needed them to work. (Seemed kind of kludgey or too complex to me.) Maybe some of them could have worked? But I found a solution that I like that works best for me, which is pretty easy. (Except for some of the back end configuration that may be needed if it is is not already setup.)
Background:
I am running a SQLServer Job which invokes Powershell to run a script which backups databases, copies backup files, and deletes older backup files, with parameters passed into it. Our server is configured to allow PowerShell to run and under the pre-setup User account with SQL Server Admin and dbo privileges in an Active Directory account to allow it to see various places on our Network as well.
But we don't want it to take the resources away from the main server. The PowerShell script that was to be run would backup the database Log file and then use the another server to asynchronously copy the file itself and not make the SQL Server Job/PowerShell wait for it. We wanted it to happen right after the backup.
Here is my new way, using WMI, using Windows Integrate Security:
$ComputerName = "kithhelpdesk"
([Wmiclass]'Win32_Process').GetMethodParameters('Create')
Invoke-WmiMethod -ComputerName RemoteServerToRunOn -Path win32_process -Name create -ArgumentList 'powershell.exe -Command "Copy-Item -Path \\YourShareSource\SQLBackup\YourDatabase_2018-08-07_11-45.log.bak -Destination \\YourShareDestination\YourDatabase_2018-08-07_11-45.log.bak"'
Here is my new way using passed in Credentials, and building arg list variable:
$Username = "YouDomain\YourDomainUser"
$Password = "P#ssw0rd27"
$ComputerName = "RemoteServerToRunOn"
$FromFile = "\\YourShareSource\SQLBackup\YourDatabase_2018-08-07_11-45.log.bak"
$ToFile = "\\YourShareDestination\SQLBackup\YourDatabase_2018-08-07_11-45.log.bak"
$ArgumentList = 'powershell.exe -Command "Copy-Item -Path ' + $FromFile + ' -Destination ' + $ToFile + '"'
$SecurePassWord = ConvertTo-SecureString -AsPlainText $Password -Force
$Cred = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $Username, $SecurePassWord
([Wmiclass]'Win32_Process').GetMethodParameters('Create')
Invoke-WmiMethod -ComputerName $ComputerName -Path win32_process -Name create -ArgumentList $ArgumentList -Credential $Cred
We think that this above one is the preferred one to use.
You can also run a specific powershell that will do what you want it to do (even passing in parameters to it):
Invoke-WmiMethod -ComputerName RemoteServerToRunOn -Path win32_process -Name create -ArgumentList 'powershell.exe -file "C:\PS\Test1.ps1"'
This example could be changed to pass in parameters to the Test1.ps1 PowerShell script to make it more flexible and reusable. And you may also want to pass in a Credential like we used in a previous example above.
Help configuring WMI:
I got the main gist of this working from: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/invoke-wmimethod?view=powershell-5.1
But it may have also needed WMI configuration using:
https://helpcenter.gsx.com/hc/en-us/articles/202447926-How-to-Configure-Windows-Remote-PowerShell-Access-for-Non-Privileged-User-Accounts?flash_digest=bec1f6a29327161f08e1f2db77e64856b433cb5a
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/enable-psremoting?view=powershell-5.1
Powershell New-PSSession Access Denied - Administrator Account
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/invoke-wmimethod?view=powershell-5.1 (I used to get how to call Invoke-WmiMethod).
https://learn.microsoft.com/en-us/powershell/scripting/core-powershell/console/powershell.exe-command-line-help?view=powershell-6 (I used to get syntax of command line)
I didn't use this one, but could have: How to execute a command in a remote computer?
I don't know for sure if all of the steps in the web articles above are needed, I suspect not. But I thought I was going to be using the Invoke-Command PowerShell statement to copy the files on a remote server, but left my changes from the articles above that I did intact mostly I believe.
You will need a dedicated User setup in Active Directory, and to configure the user accounts that SQL Server and SQL Server Agent are running under to give the main calling PowerShell the privileges needed to access the network and other things to, and can be used to run the PowerShell on the remote server as well. And you may need to configure SQLServer to allow SQL Server Jobs or Stored Procedures to be able to call PowerShell scripts like I did. But this is outside the scope of this post. You Google other places on the internet to show you how to do that.