so I want to trigger an action every time a new event occurs in the ForwardedEvents Log in windows with my powershell script.
I found the following code to subscribe to Application Log, but this does not work for the ForwardedEvent log
$Name = 'Application'
# get an instance
$Log = [System.Diagnostics.EventLog]$Name
# determine what to do when an event occurs
$Action = {
# do something when a new ecent occurs
}
# subscribe to its "EntryWritten" event
$job = Register-ObjectEvent -InputObject $log -EventName EntryWritten -SourceIdentifier 'NewEventHandler' -Action $Action
The error I get when trying this code with "ForwardedEvents" as name is:
Register-ObjectEvent : The event log 'ForwardedEvents' on computer '.' does not exist.
Thank you for any help!
EDIT:
My ForwardedEvent Log is active and filled with events. I use Windows 10 for this.
Related
I am new to PowerShell and I am trying to use the System.IO.FileSystemWatcher to monitor the presence of a file in a specified folder. However, as soon as the file is detected I want to stop monitoring this folder immediately and stop the FileSystemWatcher. The plan is to incorporate the PowerShell script into a SQL Agent to enable users to restore their own databases. Basically I need to know the command to stop FileSystemWatcher from monitoring as soon as one file is found. Here is the script so far.
### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "C:\TriggerBatch"
$watcher.Filter = "*.*"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
### DEFINE ACTIONS AFTER A EVENT IS DETECTED
$action = { $path = $Event.SourceEventArgs.FullPath
$changeType = $Event.SourceEventArgs.ChangeType
$logline = "$(Get-Date), $changeType, $path"
Add-content "C:\log2.txt" -value $logline
}
### DECIDE WHICH EVENTS SHOULD BE WATCHED + SET CHECK FREQUENCY
$created = Register-ObjectEvent $watcher Created -Action $action
while ($true) {sleep 1}
## Unregister-Event Created ??
##Stop-ScheduledTask ??
Unregister-Event $created.Id
This will unregister the event. You will probably want to add this to the $action.
Do note that if there are events in the queue they will still be fired.
This might help too.
Scriptblocks that are run as an action on a subscribed event have access to the $Args, $Event, $EventArgs and $EventSubscriber automatic variables.
Just add the Unregister-Event command to the end of your scriptblock, like so:
### DEFINE ACTIONS AFTER A EVENT IS DETECTED
$action = { $path = $Event.SourceEventArgs.FullPath
$changeType = $Event.SourceEventArgs.ChangeType
$logline = "$(Get-Date), $changeType, $path"
Add-content "C:\log2.txt" -value $logline
Unregister-Event -SubscriptionId $EventSubscriber.SubscriptionId
}
This is the pattern for an event that only performs an action once and then cleans itself up.
It's difficult to effectively explore these automatic variables since they are within the scope of a Job, but you can futz with them by assigning them to global variables while you are sketching out your code. You may also get some joy with Wait-Debugger and Debug-Runspace. In the case of the $EventSubscriber variable, it returns the exact object you get if you run Get-EventSubscriber (having created a single subscription already). That's how I found the SubscriptionId property.
If you want to stop/unregister all registered events you can call
Get-EventSubscriber|Unregister-Event
I have a PowerShell script that uses an Asynchronous Timer Event (background process) to measure how long a certain condition has been occurring before taking appropriate action.
This is working perfectly fine when I run the script inside PowerGUI but when I run the script using dot-sourcing or run it via a batch file the Timer Event actions are not firing.
Here is a code snippet.
$timer = New-Object System.Timers.Timer
$timer.Interval = 10000
$timer.AutoReset = $true
$timeout = 0
$action = {
"timeout: $timeout" | Add-Content $loglocation
<more stuff here>
$timer.stop()
}
$start = Register-ObjectEvent -InputObject $timer -SourceIdentifier TimerElapsed -EventName Elapsed -Action $action
$timer.start()
while(1)
{
<do some testing here>
}
So when it works, I will see the "timeout: XX" output every 10 seconds written to the log. But this is only happening when run inside the editor. When I run it via batch file nothing happens (although I can confirm the while loop is processing fine).
So my question is why is my experience different when I'm running the script inside PowerGUI versus via command line? My thought is there might be an issue with scoping or parallel threads but I'm not exactly sure what the issue is. Also I am not running these events inside any functions or loops.
When calling the script file, the $action script block is executed using the scope of the caller (parent scope), not the script file's scope (child scope). Therefore, variables defined within the script file are not available within the $action script block, unless they are defined to use the global scope or dot-sourced (which will make them available in the global scope). See this wonderful article for more information.
Assume the below code is contained within a file named test.ps1.
$timer = New-Object System.Timers.Timer
$timer.Interval = 10000
$timer.AutoReset = $false
$timeout = 100
$location = 'SomeLocation'
$sourceIdentifier = 'SomeIdentifier'
$action = {
Write-Host "Timer Event Elapsed. Timeout: $timeout, Location: $location, SourceIdentifier: $sourceIdentifier"
$timer.stop()
Unregister-Event $sourceIdentifier
}
$start = Register-ObjectEvent -InputObject $timer -SourceIdentifier $sourceIdentifier -EventName Elapsed -Action $action
$timer.start()
while(1)
{
Write-Host "Looping..."
Start-Sleep -s 5
}
When calling from the powershell console, when the $action script block is executed, the variables it uses will have no values.
./test.ps1
Timer Event Elapsed. Timeout: , Location: , SourceIdentifier:
If you define the variables used in the $action script block before you call the script, the values will be available when the action executes:
$timeout = 5; $location = "SomeLocation"; $sourceIdentifier = "SomeSI"
./test.ps1
Timer Event Elapsed. Timeout: 5, Location: SomeLocation, SourceIdentifier: SomeSI
If you dot-source the script, the variables defined within the script will become available in the current scope, so when the action executes, the values will be available:
. ./test.ps1
Timer Event Elapsed. Timeout: 100, Location: SomeLocation, SourceIdentifier: SomeIdentifier
If the variables would have been declared in the global scope in the script file:
$global:timeout = 100
$global:location = 'SomeLocation'
$global:sourceIdentifier = 'SomeIdentifier'
Then when the $action script block executes in the parent scope, the values will be available:
./test.ps1
Timer Event Elapsed. Timeout: 100, Location: SomeLocation, SourceIdentifier: SomeIdentifier
Like dugas' answer, but if you don't want to clutter up your PowerShell instance with extra variables or do any dot-sourcing, you can put it in a function. This also has the benefit of letting you use named parameters and makes it more modular if you want to re-use it in the future.
function Start-Timer
{
param($timeout = 5, $location = "SomeLocation", $sourceIdentifier = "SomeSI")
$timer = [System.Timers.Timer]::new()
$timer.Interval = $timeout
$timer.AutoReset = $False
$action =
{
$myArgs = $event.MessageData
$timeout = $myArgs.timeout
$location = $myArgs.location
$sourceIdentifier = $myArgs.sourceIdentifier
$timer = $myArgs.timer
Write-Host "Timer Event Elapsed. Timeout: $timeout, Location: $location, SourceIdentifier: $sourceIdentifier"
$timer.Stop()
Unregister-Event $sourceIdentifier
}
# You have to pass the data this way
$passThru =
#{
timeout = $timeout;
location = $location;
sourceIdentifier = $sourceIdentifier;
timer = $timer;
}
Register-ObjectEvent -InputObject $timer -EventName Elapsed -SourceIdentifier Tick -Action $action -MessageData $passThru | Out-Null
$timer.Start()
}
Then you can call it with named parameters:
Start-Timer -location "NewLocation"
A disadvantage to purely using this approach is that if the Handler uses a large number of variables from the containing scope, the code will get messy.
I'm setting a handler for the InstantMessageReceived event, but it only seems to fire on outgoing text messages, not incoming. Here is the code I'm running:
# Register the app with Growl
$icon = "https://docs.google.com/uc?export=download&id=0B1Weg9ZlwneOZmY2b1NSVXJ0Q2s"
$types = '"new-im","new-call","invitation","share"'
& 'C:\Program Files (x86)\Growl for Windows\growlnotify.exe' /a:Lync /ai:$icon /r:$types "Registration."
#We just need the Model API for this example
import-module "C:\Program Files (x86)\Microsoft Lync\SDK\Assemblies\Desktop\Microsoft.Lync.Model.Dll"
#Get a reference to the Client object
$client = [Microsoft.Lync.Model.LyncClient]::GetClient()
#Set the client to reference to the local client
$self = $client.Self
# What do we do here?
$conversationMgr = $client.ConversationManager
# Register events for existing conversations.
$i = 0
for ($i=0; $i -lt $conversationMgr.Conversations.Count; $i++) {
Register-ObjectEvent -InputObject $conversationMgr.Conversations[$i].Modalities[1] -EventName "InstantMessageReceived" `
-SourceIdentifier "new im $i" `
-action {
$message = $EventArgs.Text
Write-Host "DEBUG: New incoming IM - $message"
# Try to get the name of the person...
$contactInfo = $Event.Sender.Conversation.Participants[1].Contact.GetContactInformation([Microsoft.Lync.Model.ContactInformationType[]] #("FirstName", "LastName", "DisplayName", "PrimaryEmailAddress", "Photo", "IconUrl", "IconStream"))
$name = " "
if ($contactInfo.Get_Item("FirstName")) { $name = $contactInfo.Get_Item("FirstName") + " " + $contactInfo.Get_Item("LastName") + ":" }
elseif ($contactInfo.Get_Item("DisplayName")) { $name = $contactInfo.Get_Item("DisplayName") + ":"}
else { $name = $contactInfo.Get_Item("PrimaryEmailAddress") + ":" }
# We need to check if the Lync window (conversation?) has focus or not.
if (1) {
# We need to send our growl notification.
& 'C:\Program Files (x86)\Growl for Windows\growlnotify.exe' /a:Lync /n:new-im /t:"New Instant Message" "$name $message"
}
}
}
# If this exits, no more events.
while (1) { }
Every time I type out an IM message to someone else, it does what I'm trying to do for incoming messages. But nothing ever fires for those, just outgoing. I've been through all the documentation, and there aren't any other candidate events, I'm sure it's this one. But the Modality object just stores some stuff about whether it's an IM or screensharing and the like, nothing useful.
http://msdn.microsoft.com/en-us/library/lync/microsoft.lync.model.conversation.instantmessagemodality_di_3_uc_ocs14mreflyncclnt_members(v=office.14).aspx
Where am I screwing up on this? I prefer answers in Powershell, but I don't think this is a problem specific to Powershell, so if you know how to do it in C# or Visual Basic or something like that, I'd appreciate that too.
I don't have Lync so I can test this myself, but take a look at this link where it shows how to use the API.
The problem is(from what I understand) that there is a modality per participant per media. So for a conversation with two members using only text, there will be 2 modalities, one for incoming messages(from the remote participant) and one for outgoing. This is specified here in
Occurs when an instant message is received, or sent if the InstantMessageModality belongs to the local participant.
Source: MSDN
When you register your object-event, you register it to "your modality", and not the remote modality. To fix it it seems to you need to take each conversation from the manager, look at each participant except the one representing you (check the IsSelf property). Then take the modality from the participants(except yourself) and register for the InstantMessageReceived event.
At least that's what I got out of it, but as said I have no experience with Lync so I could easily be wrong.
My guess at how it could be done(VERY untested):
# What do we do here? You get the manager the keeps track of every conversation
$conversationMgr = $client.ConversationManager
# Register events for existing conversations.
#You may need to use '$conversation in $conversationMgr.GetEnumerator()'
foreach ($conversation in $conversationMgr) {
#Get remote participants
$conversation.Participants | where { !$_.IsSelf } | foreach {
#Get IM modality
$textmod = [InstantMessageModality]($_.Modalities[ModalityTypes.InstantMessage])
Register-ObjectEvent -InputObject $textmod -EventName "InstantMessageReceived" `
-SourceIdentifier "new im $i" `
-action {
#...
}
}
}
Does anyone know is there a way to make Exchange server 2010 execute some program upon mail receive for some mailbox ?
For example:
I have mails test1#example.com and test2#example.com
I want to execute program1.exe when mail arrives to test1#example.com
and execute program2.exe when mail arrives to test2#example.com
I have looked all options in Exchange management console, and didn't find anything similar.
I made something similar using EWS and powershell. You can download EWS
here
Then you can create an script in powershell that use the Exchange web service
This is the example of my script:
$MailboxName = "mail#domain.com"
$dllpath = "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"
[void][Reflection.Assembly]::LoadFile($dllpath)
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)
$service.TraceEnabled = $false
$service.Credentials = New-Object System.Net.NetworkCredential("name","password", "domain")
$service.Url="https://mail.yourdomain.com.au/ews/exchange.asmx"
try{
$fldArray = new-object Microsoft.Exchange.WebServices.Data.FolderId[] 1
$Inboxid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)
$fldArray[0] = $Inboxid
$stmsubscription = $service.SubscribeToStreamingNotifications($fldArray, [Microsoft.Exchange.WebServices.Data.EventType]::NewMail)
$stmConnection = new-object Microsoft.Exchange.WebServices.Data.StreamingSubscriptionConnection($service, 30);
$stmConnection.AddSubscription($stmsubscription)
Register-ObjectEvent -inputObject $stmConnection -eventName "OnNotificationEvent" -Action {
foreach($notEvent in $event.SourceEventArgs.Events){
[String]$itmId = $notEvent.ItemId.UniqueId.ToString()
$message = [Microsoft.Exchange.WebServices.Data.EmailMessage]::Bind($event.MessageData,$itmId)
IF ($message.Subject -eq "execprocess"){
Start-Process "mybat.bat"
}
}
} -MessageData $service
}catch [Exception] {
Get-Date | Out-File C:\logs\logError.txt -Append
"Error : "+ $_.Exception.Message
}
Register-ObjectEvent -inputObject $stmConnection -eventName "OnDisconnect" -Action {$event.MessageData.Open()} -MessageData $stmConnection
$stmConnection.Open()
Then, run the 2 scripts one for every account you need to monitoring.
See the original example here--> Source
Exchange has no out-of-box way to do this. You might want to look at using Exchange Web Services (EWS) subscriptions in an external service to do this.
One way I accomplished this is by writing a powershell script using outlook com objects to scan the inbox for certain criteria and execute a process based on what it finds.
I've been tasked with building a powershell script with a GUI which enables users to install network printers. I've succesfully managed to do so, but I cannot meet the requirement that the user be shown a 'please wait' window whilst the printers install. If I switch to the window from the main thread, the GUI hangs. If I move showing the window to a seperate job, I'm never able to close the window again. Here's my attempt:
$waitForm = New-Object 'System.Windows.Forms.Form'
$CloseButton_Click={
# open "please wait form"
Start-Job -Name waitJob -ScriptBlock $callWork -ArgumentList $waitForm
#perform long-running (duration unknown) task of adding several network printers here
$max = 5
foreach ($i in $(1..$max)){
sleep 1 # lock up the thread for a second at a time
}
# close the wait form - doesn't work. neither does remove-job
$waitForm.Close()
Remove-Job -Name waitJob -Force
}
$callWork ={
param $waitForm
[void][reflection.assembly]::Load("System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
$waitForm = New-Object 'System.Windows.Forms.Form'
$labelInstallingPrintersPl = New-Object 'System.Windows.Forms.Label'
$waitForm.Controls.Add($labelInstallingPrintersPl)
$waitForm.ClientSize = '502, 103'
$labelInstallingPrintersPl.Location = '25, 28'
$labelInstallingPrintersPl.Text = "Installing printers - please wait..."
$waitForm.ShowDialog($this)
}
Does anyone know how I can dismiss the $waitForm window when the long-running task has concluded?
You could try to run the Windows Forms dialog on the main thread and do the actual work in a background job:
Add-Type -Assembly System.Windows.Forms
$waitForm = New-Object 'System.Windows.Forms.Form'
$labelInstallingPrintersPl = New-Object 'System.Windows.Forms.Label'
$waitForm.Controls.Add($labelInstallingPrintersPl)
$waitForm.ClientSize = '502, 103'
$labelInstallingPrintersPl.Location = '25, 28'
$labelInstallingPrintersPl.Text = "Installing printers - please wait..."
$waitForm.ShowDialog($this)
Start-Job -ScriptBlock $addPrinters | Wait-Job
$waitForm.Close()
$addPrinters = {
$max = 5
foreach ($i in $(1..$max)) {
sleep 1 # lock up the thread for a second at a time
}
}
This first answer was correct, create the form on the main thread and perform the long running task on a separate thread. The reason it doesn't execute the main code until after the form is dismissed is because you're using the 'ShowDialog' method of the form, this method haults subsequent code execution until the form is closed.
Instead use the 'show' method, code execution will continue, you should probably include some event handlers to dispose of the form
Add-Type -Assembly System.Windows.Forms
$waitForm = New-Object 'System.Windows.Forms.Form'
$labelInstallingPrintersPl = New-Object 'System.Windows.Forms.Label'
$waitForm.Controls.Add($labelInstallingPrintersPl)
$waitForm.ClientSize = '502, 103'
$labelInstallingPrintersPl.Location = '25, 28'
$labelInstallingPrintersPl.Text = "Installing printers - please wait..."
$waitForm.Add_FormClosed({
$labelInstallingPrintersPl.Dispose()
$waitForm.Dispose()
})
$waitForm.Show($this)
Start-Job -ScriptBlock $addPrinters | Wait-Job
$waitForm.Close()
$addPrinters = {
$max = 5
foreach ($i in $(1..$max)) {
sleep 1 # lock up the thread for a second at a time
}
}
How about adding a Windows.Forms.Progressbar to the main GUI window? Update its value step by step when adding printers, so users will see that the application is working.