Scheduling a Powershell process does not yield the same results as when I run it manually - powershell

I wrote a small PowerShell script that I am using to query the Server Log, clean the return values and use some of the results to perform some server maintenance. However, when I schedule the save to file piece is not writing the whole content to the file and it is getting truncated, just like what I ma posting below, exactly. As you can observe, the end of the file is truncated with three dots added to replace the missing values:
Login failed for user 'sa'. Reason: An error occurred while evaluating the password. [CLIENT: 2...
However, if I run the code manually with Local Admin access, the content gets saved to the local file like this, exactly:
Login failed for user 'sa'. Reason: An error occurred while evaluating the password. [CLIENT: 112.103.198.2]
Why is this the case when I schedule the process or PS file to run under a schedule. BTW, I tried to run it under the SYSTEM context with full or highest privileges and even used the same Admin account that I use to run it manually to schedule and still do nt get the full content of the event that I save.
This is creating an issue and I am not able to use the content to process the IP.
Here is the PS code that I am using to query and save the content to file:
$SQL = 'C:\SQL.txt'
Remove-Item $SQL -ErrorAction Ignore
Get-EventLog -LogName Application | Where-Object {$_.EventID -eq 18456} |
Select-Object -Property Message | Out-File $SQL

The problem lies with out-file because it has a default character limit of 80 per line.
You can change it with -width property and give a value of say 200. However set-content doesn't have these limits set in. So it might be a more suitable option.
All that being said, I am not sure why it does it one way when ran manually vs another when the system runs it.
Out-file defaults to unicode when writing files
set-file defaults to ascii when writing files

Related

Get Location of Error in PowerShell script (System.Management.Automation)

I have a powershell script running in a c# service (System.Management.Automation). Sometimes seldom I get the following error:
Unable to cast object of type 'System.Double' to type 'System.String'.
Is is somehow possible to include the variable name, line number or anything that could help me find the error location? E.g. code that runs in a powershell terminal shows me the corresponding line.
It's worth thinking through your error handling and error logging approach to avoid issues like this. Consider whether you want your error to be terminating or non-terminating, and whether you want to suppress or log the error.
Much has already been written on SO on detecting and handling script errors, and there is also ample MS documentation on Trap and Try-Catch-Finally, so I don't want to go over that ground.
Depending on where the script runs from and with what credentials, one trick would be to record errors to a logfile you could check when these errors happen:
# Create C:\Temp directory if it doesn't exist
New-Item "C:\Temp" -Force -ItemType Directory | Out-Null
# Simple trap to catch all errors
trap {
# This is a single-line output more suitable for on-screen error messages
Write-Output "Error encountered: $_ $($_.InvocationInfo.PositionMessage.Split("`n")[0])"
# This is a multi-line output to make it easier to find the error location in a file
Add-Content -LiteralPath "C:\Temp\logfiletest.txt" -Value $_,$_.InvocationInfo.PositionMessage
return
}
# Div/0 triggers the trap to test behaviour
1/0
The trap alone (or something like it) should catch the error you're hitting. Though in practice (not knowing how often you run it or how many errors you generate) you'd want to be wary that you don't inadvertently start generating excessively large logfiles on C: - you could avoid this by selecting a lower-risk drive/location and putting a dynamic date into the filename.

PowerShell - showing a message on remote computer screen

When I am running commands or installing software remotely using PowerShell - Invoke-Command etc I would like sometimes to be able to show a message on the remote screen so the user knows something is happening, or when work done etc.
I would like to if possible make this message look as professional as possible, e.g. better than just a standard winform message box if it can be done? perhaps more the style of the Windows 10 ones with coloured background and use of image if possible.
Spent a while googling but most seem to relate to using obsolete methods such as net-send or using msg.exe.
Thanks
https://michlstechblog.info/blog/powershell-show-a-messagebox/
So the issue really isnt creating the messagebox itself, its having it show on the users session.
So when you run a command against a system, youre using your creds to run the command therefore it wont show in the users session. You can get around this by running it in the users context using a task scheduler. I have a script that does all this for you but, id hate to recreate the wheel and will have to wait till monday (when im at work) to post it here.
It accepts user input in your sessions that outputs it to a vbs, which then copies it over the message to the users machine, and a task schedule is set to run immediately for the user thats logged in.
edit: The script is this without the task scheduler. I just invoke gwmi win32_computersystem | Select -ExpandProperty username to get the current user logged in and add it to the task.
#Prompt for messge
$strMSG = Read-Host -Prompt "Enter message"
#deleting vbs if it exists
del C:\brief\test.vbs
#creating vbs from scratch so it doesnt override
New-Item C:\brief\test.vbs
#Appending each the values to a seperate line
Add-Content C:\brief\test.vbs 'Set objShell = Wscript.CreateObject("WScript.Shell")'
Add-Content C:\brief\test.vbs "strText = `"$strMSG`""
Add-Content C:\brief\test.vbs 'intButton = objShell.Popup(strText,0,"Computer Support",48)'
#calling on the script
& cscript C:\brief\test.vbs
Found a great solution here which appears on quick testing to work well for displaying a toast notification on a remote machine
https://smsagent.blog/2019/06/11/just-for-fun-send-a-remote-toast-notification/

Get-EventLog not parsing Message when run by SYSTEM user

Problem
I am trying to schedule a job that monitors events on remote machines.
I wrote the script based on the Get-EventLog command and it works properly when run by my account. But when I run the Get-EventLog as SYSTEM user, the .Message attribute of the returned objects shows the following error:
The description for Event ID '4724' in Source 'Microsoft-Windows-Security-Auditing' cannot be found. The local computer may not have the necessary registry information or message DLL files to display the message, or you may not have permission to access them. The following information is part of the event: {somedata}
When I use the Get-WinEvent command as SYSTEM user, the problem does not appear and the .Message part displays properly.
I would stick with Get-WinEvent, especially since the data is much easier to parse (thanks to the ToXML() method), but the Get-EventLog happens to be terribly faster :(
Question
Does anyone have any idea why the Get-EventLog fails to render .Message when run by SYSTEM user and perhaps how to fix it?
To avoid obvious answers:
the COMPUTER$ account is member of DOMAIN\Event Log Readers group,
the COMPUTER$ account does have the read privileges over the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Security on remote machines,
obviously, the registry entries for Microsoft-Windows-Security-Auditing and related DLL's are identical on both the source and target computers.
Try:
Get-WinEvent -LogName “Microsoft-Windows-Security-Auditing” | where ID -eq 4724 | select-object -ExpandProperty Message

Starting an exe file with parameters on a remote PC

We have a program running on about 400 PCs (All W7). This program is called Wisa.
We receive regular updates for this program, named something like wisa_update1.0.exe, wisa_update1.1.exe, wisa_update2.0.exe, etc. The users can not do the update themself due to account restrictions.
We manage to do the update once and distribute it with a copy-item to all PCs. Then with Enter-PSSession I can go to each PC and update the program with the following command:
wisa_update3.0 /verysilent
(with the argument /verysilent no questions are asked)
This is already a major gain in time, but I want to do the update more automatically.
I have a file "pc.txt" with all 400 PCs in it. I use this file already for the Copy-Item via Get-Content. Now I want to use this file to do the updates with the above command, but I can't find a good way to use a remote executable with a parameter in PowerShell.
What you want to do is load get-content -Path $PClist and then run your script actions in a foreach. You'll want to adapt this example to your own script:
$PClist = 'c:\pc.txt'
$aComputers = Get-Content -Path $PClist
foreach ($Computer in $aComputers)
{
code actions to perform
}
Also you can use multithreading and get it over with fraction of time (provided you have a good machine). The below mentioned link explains how to do it well.
http://www.get-blog.com/?p=22

Automatic conversion of evtx to plaintext logs

I have a Windows Server 2008 running MicroSoft Exchange. The Audit Logs are stored in evtx and I am trying to export the logs to a 3rd party collector. The agents we have used (Snare Epilog, open source among them) do not recognized the evtx format and do not forward them to the collecting server.
I am attempting to implement a workaround via Powershell and Task Scheduler. The problem I am facing is that while I can access the evtx and save it as a .txt, I am reparsing the entire log every time. However, I would like to only send the new events every 5 minutes or less.
The code I am using is this:
$File = "C:\text.txt; Get-WinEvent -Path C:\test.evtx | Format-Table -AutoSize | Out-File $File -append -width 750
I really appreciate the help!
You could use Get-EventLog, rather than Get-WinEvent, then use the After parameter to only get the last five minutes of events or better still keep track of the most recent event message you have seen.
Here's how to get the last five minutes of the Application log.
Get-EventLog -LogName Application -After $((Get-Date).AddMinutes(-5))