{"id":13601,"date":"2011-06-17T00:01:00","date_gmt":"2011-06-17T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2011\/06\/17\/manage-event-subscriptions-with-powershell\/"},"modified":"2022-10-18T05:40:35","modified_gmt":"2022-10-18T12:40:35","slug":"manage-event-subscriptions-with-powershell","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/manage-event-subscriptions-with-powershell\/","title":{"rendered":"Manage Event Subscriptions with PowerShell"},"content":{"rendered":"<p><strong>Summary<\/strong>: Bruce Payette shows how to manage event subscriptions with Windows PowerShell.<\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, here. I am really excited about the idea I had for this week, and I hope you will be too. I asked Candace Gillhoolley at Manning Press about posting some sample works from some of the Manning Press library of books. She responded enthusiastically and shared five samples that we will post this week. Today is part two of two parts from Bruce Payette and <em>Windows PowerShell in Action<\/em>. See <a target=\"_blank\" href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2011\/06\/16\/use-asynchronous-event-handling-in-powershell.aspx\" rel=\"noopener\">yesterday\u2019s blog post<\/a> for part 1.<\/p>\n<h2><a href=\"http:\/\/www.manning.com\/payette2\/\">Windows PowerShell in Action, Second Edition<\/a><\/h2>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5086.hsg-6-17-11-1_2E3845F1.jpg\"><img decoding=\"async\" height=\"190\" width=\"154\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4010.hsg-6-17-11-1_thumb_78BF83CB.jpg\" alt=\"Image of book cover\" border=\"0\" title=\"Image of book cover\" style=\"padding-left: 0px;padding-right: 0px;padding-top: 0px;border: 0px\" \/><\/a><\/p>\n<p>By Bruce Payette<\/p>\n<p>The key difference between event-based scripting and traditional procedural scripting is that, instead of an activity being executed as a result of an action in the script, a script (or at least a portion of it) is executed as a result of an action by the system.. In this article based on chapter 20 of <a target=\"_blank\" href=\"http:\/\/www.manning.com\/payette2\/\" rel=\"noopener\">Windows PowerShell in Action, Second Edition<\/a>, author Bruce Payette discusses asynchronous event-handling models in PowerShell. To save 35% on your next purchase use Promotional Code <strong>payette22035<\/strong> when you check out at <a target=\"_blank\" href=\"https:\/\/www.manning.com\" rel=\"noopener\">www.manning.com<\/a>.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2867.hsg-6-16-11-1_0FA45293.jpg\"><\/a><\/p>\n<p><strong><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/5417.manning_534AC468.png\"><img decoding=\"async\" height=\"26\" width=\"154\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8535.manning_thumb_7A18DAA8.png\" alt=\"manning\" border=\"0\" title=\"manning\" style=\"padding-left: 0px;padding-right: 0px;padding-top: 0px;border-width: 0px\" \/><\/a><\/strong><\/p>\n<h2><strong>Managing event subscriptions<\/strong><\/h2>\n<p>In this section, you\u2019ll see how to find your event subscriptions and how to remove them when you\u2019re done with them. Being able to remove them is important because event subscriptions persist in the session until explicitly removed.<\/p>\n<p><strong>Listing event subscriptions<\/strong><\/p>\n<p>Of course, before you can remove a subscription, you have to find it. Windows PowerShell provides the <strong>Get-EventSubscriber<\/strong> to do this. Let\u2019s use it to look at the subscription you registered in the previous section (see <a target=\"_blank\" href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2011\/06\/16\/use-asynchronous-event-handling-in-powershell.aspx\" rel=\"noopener\">yesterday\u2019s Hey! Scripting Guy blog<\/a>):<\/p>\n<pre>PS (1) > Get-EventSubscriber >\n\nSubscriptionId\u00a0: 1\nSourceObject : System.Timers.Timer \nEventName : Elapsed\nSourceIdentifier : fca4b869-8d5a-4f11-8d45-e84af30845f1\nAction : System.Management.Automation.PSEventJob \nHandlerDelegate : \nSupportEvent: False \nForwardEvent: False \n<\/pre>\n<p>The <strong>Get-EventSubscriber<\/strong> cmdlet returns <strong>PSEventSubscriber<\/strong> objects, which have complete information about the registration: the object generating the event, the action to execute, and so on. There are a couple of interesting properties to note in this output. Because you didn\u2019t give the subscription a friendly name using <strong>-Source-Identifier<\/strong> when you created it, the <strong>Register-ObjectEvent<\/strong> generated one for you. This autogenerated name is the string representation of a GUID, so you know it\u2019s unique (but not very friendly). The other thing to notice is that the action shows up as a PowerShell <strong>Job<\/strong> object.<\/p>\n<p><strong>Removing event subscriptions<\/strong><\/p>\n<p>Now that you can list the event subscriptions, you can set about removing them. The cmdlet to do this is not <strong>Unsubscribe-Event<\/strong> because unsubscribe isn\u2019t on the approved verbs list and it\u2019s not what you want to do anyway. You registered event subscriptions with <strong>Register-ObjectEvent<\/strong>, so what you need to do is unregister the subscription, which you\u2019ll do with Unregister-Event. The cmdlet noun in this case is Event, not <strong>ObjectEvent<\/strong>, because you can use a common mechanism to unregister any kind of event. It\u2019s only the registration part that varies. The rest of the eventing cmdlets remain the same.<\/p>\n<p>When you\u2019re unregistering an event subscription, there are two ways of identifying the event to unregister: by the <strong>SubscriptionId<\/strong> property or by the <strong>Source-Identifier<\/strong>. The subscription ID is simply an integer that\u2019s incremented each time an event subscription is created. Because you didn\u2019t give your event registration a friendly name, you\u2019ll use the <strong>SubscriptionId<\/strong> to unregister it:<\/p>\n<pre>> PS (4) > Unregister-Event -SubscriptionId 1 -Verbose >\n\nVERBOSE: Performing operation \"Unsubscribe\" on Target \"Event\nsubscription 'timertest2'\n<\/pre>\n<p>Note that you included the <strong>-Verbose<\/strong> flag in this command so that you could see something happening. Let\u2019s try running the command again<\/p>\n<pre>> PS (5) > Unregister-Event -SubscriptionId 1 >\n\nUnregister-Event : Event subscription with identifier '1' does not >\nexist. >\nAt line:1 char:17 >\n+ Unregister-Event &lt;&lt;&lt;&lt; -SubscriptionId 1 >\n+ CategoryInfo : InvalidArgument: (:) [Unregister-Event >\n], ArgumentException >\n+ FullyQualifiedErrorId : INVALID_SUBSCRIPTION_IDENTIFIER, >\nMicrosoft.PowerShell.Commands.UnregisterEventCommand >\n<\/pre>\n<p>\u2026and it results in an error. The <strong>Unregister-Event<\/strong> cmdlet is silent as long as nothing goes wrong. If something does go wrong, you get an error.<\/p>\n<p>We\u2019ve covered the basics of creating and managing event subscriptions. But before the handlers for these events can do much useful work, they\u2019ll need access to additional information. In the next section, you\u2019ll write more sophisticated handlers and see how they can use the automatic variables provided by the eventing subsystem.<\/p>\n<h2>Asynchronous event handling with scriptblocks<\/h2>\n<p>In this section, we\u2019ll look at the automatic variables and other features that PowerShell provides to allow scriptblocks to be used as effective event handlers.<\/p>\n<h3>Automatic variables in the event handler<\/h3>\n<p>In PowerShell eventing, the scriptblock that handles the event action has access to a number of variables that provide information about the event being handled:<\/p>\n<p><code>&gt; $event, $eventSubscriber, $sender, $sourceEventArgs, and $sourceArgs<\/code><\/p>\n<p>These variables are described in table 2.<\/p>\n<p><strong>Table 2<\/strong> The automatic variables available in the event handler scriptblock<\/p>\n<table cellpadding=\"0\" cellspacing=\"0\" border=\"1\">\n<tbody>\n<tr>\n<td width=\"160\" valign=\"top\">\n<p>\n          Variable\n        <\/p>\n<\/td>\n<td width=\"268\" valign=\"top\">\n<p>\n          Description\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"160\" valign=\"top\">\n<p>\n          $event\n        <\/p>\n<\/td>\n<td width=\"268\" valign=\"top\">\n<p>\n          This variable contains an object of type System.Management.Automation.PSEventArgs that represents the event that\u2019s being handled. It allows you to access a wide variety of information about the event, as you\u2019ll see in an example. The value of this variable is the same object that the Get-Event cmdlet returns.\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"160\" valign=\"top\">\n<p>\n          $eventSubscriber\n        <\/p>\n<\/td>\n<td width=\"268\" valign=\"top\">\n<p>\n          This variable contains the PSEventSubscriber object that represents the event subscriber of the event that\u2019s being handled. The value of this variable is the same object that the Get-EventSubscriber cmdlet returns.\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"160\" valign=\"top\">\n<p>\n          $sender\n        <\/p>\n<\/td>\n<td width=\"268\" valign=\"top\">\n<p>\n          The value in this variable is the object that generated the event. This variable is a shortcut for $EventArgs.Sender.\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"160\" valign=\"top\">\n<p>\n          $sourceEventArgs\n        <\/p>\n<\/td>\n<td width=\"268\" valign=\"top\">\n<p>\n          Contains objects that represent the arguments of the event that\u2019s being processed. This variable is a shortcut for $Event.SourceArgs.\n        <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td width=\"160\" valign=\"top\">\n<p>\n          $sourceArgs\n        <\/p>\n<\/td>\n<td width=\"268\" valign=\"top\">\n<p>\n          Contains the values from $Event.SourceArgs. Like any other scriptblock, if there is a param statement, the parameters defined by that statement will be populated and $args will only contain leftover values for which there were no parameters.\n        <\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Let\u2019s write a quick test event handler to see what\u2019s in the object in <strong>$Event<\/strong>. You\u2019ll use the timer event again:<\/p>\n<pre>> PS (1) > $timer = New-Object System.Timers.Timer -Property @{ >\n\n\n>> Interval = 1000; Enabled = $true; AutoReset = $false } >\n>> \n<\/pre>\n<p>In the event subscription action, you\u2019ll display the contents of the event object:<\/p>\n<pre>PS (2) > Register-ObjectEvent $timer Elapsed -Action { >\n>> $Event | Out-Host >\n>> } >\n>>  >\n  >\nId          Name                State      HasMoreData      Location >\n--          ----                -----      -----------      -------- >\n4           9e3586c3-534...     NotStarted False  >\n<\/pre>\n<p>You\u2019ll start the timer to generate the event:<\/p>\n<pre>PS (3) > $timer.Start() >\nPS (4) > >\n  >\nComputerName     : >\nRunspaceId       : 373d0ee9-47a5-4ceb-89e5-61e6389d6838 >\nEventIdentifier  : 7 >\nSender           : System.Timers.Timer >\nSourceEventArgs  : System.Timers.ElapsedEventArgs >\nSourceArgs       : {System.Timers.Timer, System.Timers.ElapsedEv >\nentArgs} >\nSourceIdentifier : 9e3586c3-534b-465a-84b3-7404110a0f12 >\nTimeGenerated    : 8\/10\/2010 12:17:40 PM >\nMessageData      : >\n<\/pre>\n<p>In this output, you see the properties on the <strong>PSEvent**** **object that correspond to the variables listed in table 2. The **Timer<\/strong> object that generated the event is available through the Sender property on the object and the <strong>$sender<\/strong> variable in the scriptblock.<\/p>\n<p>The <strong>PSEvent<\/strong> object also includes context data about the event, including the time the event occurred, the event identifier, and the <strong>RunspaceId<\/strong> this event is associated with. The <strong>ComputerName<\/strong> property is blank because this is a local event, but, in the case of a remote event, it would contain the name of the computer where the event occurred.<\/p>\n<h3>Dynamic modules and event handler state<\/h3>\n<p>Because an event can fire at any time, you could never know what variables were in scope and this, in turn, could make it hard to know what state will exist when the action is executed. Instead, you want to be able to run the event handlers in a well-defined, isolated environment. This objective aligns with the design goals for PowerShell modules, so you can leverage this feature by creating a dynamic module for the action scriptblock. The eventing subsystem does this by calling the <strong>New-BoundScriptBlockScriptblock()<\/strong> method to attach a dynamic module to the handler scriptblock.<\/p>\n<p>Beyond ensuring a coherent runtime environment for your event handler scriptblock, the module also allows it to have private state. This ability can be quite useful when you\u2019re monitoring a system\u2019s behavior over a period of time. The information can be accumulated privately and then processed once enough samples have been gathered. Let\u2019s look at an example that illustrates how this state isolation works. The following is a trivial example where you maintain a count of the number of timer events fired. Once you reach a predetermined limit, the timer will be stopped. Let\u2019s walk through the example. First, you create the <strong>Timer<\/strong> object:<\/p>\n<pre>PS (1) > $timer = New-Object System.Timers.Timer -Property @{ >\n>> Interval = 500; AutoReset = $true} >\n>>  >\n<\/pre>\n<p>As usual, subscribe to the <strong>Elapsed<\/strong> event on the timer:<\/p>\n<pre>PS (2) > Register-ObjectEvent -InputObject $timer ` >\n>> -MessageData 5 ` >\n>> -SourceIdentifier Stateful -EventName Elapsed -Action { >\n>> $script:counter += 1 >\n>> Write-Host \"Event counter is $counter\" >\n>> if ($counter -ge $Event.MessageData) >\n>> { >\n>> Write-Host \"Stopping timer\" >\n>> $timer.Stop() >\n>> } >\n>> } > $null >\n>>  >\n<\/pre>\n<p>In the handler scriptblock for this event, you\u2019re updating a script-scoped variable <strong>$script:counter<\/strong>, which holds the number of times the event has fired. This variable will only be visible within the dynamic module associated with the event, thus preventing your <strong>$counter<\/strong> from colliding with any other users of a variable called <strong>$counter<\/strong>.<\/p>\n<p>After the variable is incremented, you print the event count and then check to see if the limit has been reached. Notice that you\u2019re making use of the <strong>-MessageData<\/strong> parameter to pass the limit to the event handler, which it retrieves from the <strong>MessageData<\/strong> property on the **Event **object. Now start the timer running to see it in action:<\/p>\n<pre>PS (3) > $timer.Start() >\nPS (4) > >\nPS (5) > Event counter is 1 >\nEvent counter is 2 >\nEvent counter is 3 >\nEvent counter is 4 >\nEvent counter is 5 >\nStopping timer >\n  >\nPS (6) > >\n<\/pre>\n<p>As intended, the timer message is displayed five times and then the timer is stopped. This example can easily be modified to, for example, monitor CPU usage or process working sets over a period of time.<\/p>\n<h2>Summary<\/h2>\n<p>There are two fundamental event types: synchronous and asynchronous. In synchronous events, all activities are synchronized so that no activity is ever interrupted. Asynchronous events execute in a nondeterministic order. To deal with these asynchronous events, PowerShell includes an eventing subsystem that takes care of synchronizing all operations. The core model for eventing in PowerShell is built around the idea of event subscriptions. There are three cmdlets for creating these subscriptions: <strong>Get-ObjectEvent<\/strong>, <strong>Get-WmiEvent<\/strong>, and <strong>Get-EngineEvent<\/strong> for .NET, WMI, and PowerShell engine events respectively.<\/p>\n<p>As part of the event subscription, an action scriptblock may be specified that will be executed when the event is triggered. Context information for the event is made available to the scriptblock through the <strong>$Event<\/strong> automatic variable. Some of the properties on the object in <strong>$Event<\/strong> are also directly available through additional automatic variables.<\/p>\n<p>Thank you, Bruce.<\/p>\n<p>Well, this concludes an awesome week of guest writers from Manning Press. Join me tomorrow for the Weekend Scripter as I delve into my top ten favorite Windows PowerShell tricks.<\/p>\n<p>I invite you to follow me on <a target=\"_blank\" href=\"http:\/\/bit.ly\/scriptingguystwitter\" rel=\"noopener\">Twitter<\/a> and <a target=\"_blank\" href=\"http:\/\/bit.ly\/scriptingguysfacebook\" rel=\"noopener\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"&#109;&#97;&#x69;&#x6c;&#116;&#111;&#x3a;&#x73;c&#114;&#105;&#x70;&#x74;&#101;&#114;&#x40;&#x6d;&#105;&#99;&#x72;&#x6f;s&#111;&#102;&#x74;&#x2e;&#99;&#111;&#x6d;\">&#x73;c&#114;&#105;&#x70;&#x74;&#101;&#114;&#x40;&#x6d;&#105;&#99;&#x72;&#x6f;s&#111;&#102;&#x74;&#x2e;&#99;&#111;&#x6d;<\/a>, or post your questions on the <a target=\"_blank\" href=\"http:\/\/bit.ly\/scriptingforum\" rel=\"noopener\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><strong>Ed Wilson, Microsoft Scripting Guy<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Bruce Payette shows how to manage event subscriptions with Windows PowerShell. Microsoft Scripting Guy, Ed Wilson, here. I am really excited about the idea I had for this week, and I hope you will be too. I asked Candace Gillhoolley at Manning Press about posting some sample works from some of the Manning Press [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[266,42,56,3,4,45],"class_list":["post-13601","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-bruce-payette","tag-events-and-monitoring","tag-guest-blogger","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Bruce Payette shows how to manage event subscriptions with Windows PowerShell. Microsoft Scripting Guy, Ed Wilson, here. I am really excited about the idea I had for this week, and I hope you will be too. I asked Candace Gillhoolley at Manning Press about posting some sample works from some of the Manning Press [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/13601","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=13601"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/13601\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=13601"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=13601"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=13601"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}