Contact

Subscribe via Email

Subscribe via RSS/JSON

Categories

Recent Posts

Creative Commons Attribution 4.0 International License
© Rakhesh Sasidharan

Elsewhere

Using SolarWinds to highlight servers in a pending reboot status

Had a request to use SolarWinds to highlight servers in a pending reboot status. Here’s what I did.

Sorry, this is currently broken. After implementing this I realized I need to enable PowerShell remoting on all servers for it to work, else the script just returns the result from the SolarWinds server. Will update this post after I fix it at my workplace. If you come across this post before that, all you need to do is enable PowerShell remoting across all your servers and change the script execution to “Remote Host”.

SolarWinds has a built in application monitor called “Windows Update Monitoring”. It does a lot more than what I want so I disabled all the components I am not interested in. (I could have also just created a new application monitor, I know, just was lazy).

winupdatemon-1

The part I am interested in is the PowerShell Monitor component. By default it checks for the reboot required status by checking a registry key: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired. Here’s the default script –

Inspired by this blog post which monitors three more registry keys and also queries ConfigMgr, I replaced the default PowerShell script with the following –

Then I added the application monitor to all my Windows servers. The result is that I can see the following information on every node –

winupdatemon-2

Following this I created alerts to send me an email whenever the status of the above component (“Machine restart status …”) went down for any node. And I also created a SolarWinds report to capture all nodes for which the above component was down.

winupdatemon-3

Then I assigned this to a schedule to run once in a month after our patching window to email me a list of nodes that require reboots.

 

Use PowerShell to get the names and email addresses of members of a distribution group

There are easier ways probably but this is what I use:

Using Get-WinEvent to look at Windows event logs

Playing around with Get-WinEvent today. I find it very useful, especially when dealing with remote computers (as I have to at work). Launching Event Viewer, connecting to a remote computer (or even local computer), and then sifting through logs (or creating filters to sift) seems very cumbersome when I can acheive the same results much faster via PowerShell.

As you might have seen the Event Viewer has various logs. Using Get-WinEvent you can select which logs to focus on. To get a list of available logs do the following:

Probably better to filter through format-table for neater output:

To view details of a specific log, replace * with the name (and pipe output to format-list to view all the details):

To view all events in a log do the following:

There’s a lot of output, so good to restrict the number of entries:

Strangely there’s no easy way to restrict the entries starting at a certain time. There is, but no easy switchy way. Instead you have to do the following:

We use the -FilterHashTable switch here. This takes a hash table containing the log name as well as other parameters to filter on (you can start time, end time, provider name, ID, etc, and can combine multiple parameters). In the example above I want all events from the “System” log for the last 2 hours – so I use the get-date cmdlet, use it’s method AddHours() and set the number of hours to be added as -2.

In the next example I filter all events from the “System” log with event ID 7036 starting from now yesterday up to an hour ago.

To change the displayed order of events from newest-first to oldest-first use the -Oldest switch.

Beats piping the output to a sort cmdlet to reverse-sort!

Similar to how you can list the lognames you can list providers too:

This gives a list of the providers (the applications or sources that log events) and the logs to which they send events. The list can be huge, so a good idea is to pipe the output through where-object to filter by the providers you are interested in:

Note: The case doesn’t really matter above (“PowerShell”) because the -match operator is case in-sensitive by default. Use -cmatch if you care about case.

Back to the -FormatHashTable switch: this one takes a “ProviderName” key too containing the name of the provider you wish to filter on. The name can contain wildcards if you want to search multiple providers. For instance, the example below searches the “Application” log for all entries by the Citrix providers (there are many hence the wildcard) logged in the past three hours:

Note: In the example above, I can omit the logname. The hashtable requires either the log name or the provider name (or the path to a log). If you don’t care about the log name or provider name, mention one of these but leave the value as a wildcard. Like thus:

The above example gets all logs from the past 3 hours.

The default output of Get-WinEvent includes a lot of fields. Best to use format-table or select-object to only show what you want. In the example below I use select-object to select just the Message, ID, and TimeCreated properties. Further I pipe the output to a CSV file (doing that just to show how easy it is to quickly pull some remote logs into a CSV file):

Taking PowerShell on a date

I needed to filter the event logs from a remote computer for power off events. The user was powering it off himself every now and then, but wouldn’t believe me when I said so. No problem, thanks to the event logs all his shutdown requests are logged so it’s only a matter of culling it out.

I could have user Event Viewer but PowerShell is way easier. I only need to check the System event logs for any events with ID 1074. Further, for every shutdown, events with this ID are logged twice – one starting with the line “The process C:Windowssystem32winlogon.exe …” the other starting with the line “The process Explorer.EXE has initiated the power off …” – so I’d like to filter the output to just include one of these lines. Finally, I’d also like the day of the week to be shown.

So here’s what I did:

So far so good. I use Get-WinEvent to return the event logs I want, use the Message property of these returned objects to filter logs containing just the text we want. And finally I use the TimeCreated property of these objects to display the time and day. This TimeCreated property is the subject of this post so let’s focus on that further.

First, how do we get the members and properties of the objects returned by Get-WinEvent? Here’s how:

Notice the TimeCreated property.

The TimeCreated property is an object of class DateTime. To get the day of the date instance, use the TimeCreated.DayofWeek property. Which is what I extract via the Format-Table expression in my initial code:

This post from the “Hey, Scripting Guy! Blog” is a good read on Get-WinEvent and the DateTime class.

Focussing on the DateTime class, this is the type of the object returned by the get-date cmdlet too. Although not obvious from the syntax of the get-date cmdlet, you can use it to return a DateTime object instance of the date you specify as string. Like thus:

What’s more, you can even format the string:

The -Format switch takes a format specifier. As seen above “dd” stands for the date, “ddd” stands for the day in short-hand, “dddd” stands for the date in longhand, and so on.

You can also typecast a string to the DateTime class:

The typecast technique has a limitation though in that it expects the input to be in the format of US, i.e. “MM/dd/YYYY”. The DateTime class has a static member called Parse(), however, that is smart enough the use the country/ culture of the computer.

How do we parse the date if it’s in a different format though? Both the get-date cmdlet and the [datetime]::Parse() method fail if the input string does not match the date format of the computer:

It is possible to specify the date format of the input string. This only works with the Parse() method though. Like thus (thanks to this blog post):

The Globalization.cultureinfo class has a GetCultureInfo() method, so what we are doing above is we create a variable that is an instance of the “en-US” culture and then pass this variable to the Parse() method so it knows which format to parse the input in. Of course we could have also specified the whole thing on one line:

Let’s examine the Globalization.cultureinfo class. First, let’s examine its static members and properties:

The GetCulture() method can be used to list all available cultures:

To get details about a specific culture use the GetCultureInfo() method:

The LCID corresponds to the Locale ID assigned by Microsoft. To see all the properties use the get-member cmdlet or format-list to see the values:

On the topic of dates, the DateTimeFormat property seems interesting:

Notice the ShortDatePattern above. This is exactly what the -Format switch of Get-Date cmdlet expects, so one can use this to format the output of Get-Date in a different locale. Like thus:

So now we have seen to use the [datetime]::Parse() method to parse an input string into a DateTime object while specifying the country format of the input string. We have also seen how to output the result of get-date in the format of a different country. Both methods use the [Globalization.cultureinfo] class.

Lastly, there is an easy way to parse any string – irrespective of whether it matches the format of a country or not – into a DateTime object via the ParseExact() method. This is worth mentioning as without such a method one would have to resort to regular expressions and such!

In the example below we match an input string with time “02:50PM” by specifying the format as “hh:mmtt” to the ParseExact() method.

I am not sure what the third parameter of this method is supposed to be. Most examples put it as $null. It seems to take a “culture” as input, but I am not sure how it affects the output. Instead of $null the following too seem to work: $(get-culture), [Globalization.Cultureinfo]::GetCultureInfo("en-US") (“en-GB” too works), and [Globalization.Cultureinfo]::InvariantCulture.

The format can contain other text too. Escape the characters of such text with a slash or put the text within quotes ':

And just to show what happens if the format does not match:

Thanks to these two posts for showing me ParseExact().

Find the computer from where an AD account is locked out

I want to find out where from a user account is locked out in my domain.

The manual way to do this would be to open up Event Viewer, scan the event logs on the DC for event ID 4740, open it up and see the message to identify the machine from where this account was locked out. But using PowerShell we can obviously automate this way easily!

First things first: how to scan the event logs using PowerShell? You can use either of Get-EventLog or Get-WinEvent. The former only works on the classic event logs while the latter works on both the classic and newer event logs such as those found in Windows Vista and later. I find the latter faster too, so let’s use that. Here’s me running that cmdlet to filter the security logs for event ID 4740 on a remote DC:

There’s only one event here so the details of that are returned. It gives us all the info we need at a glance: user name “User01” is locked thanks to a bad password attempt from the computer “MANGO”.

Neat!

Now let’s go about making this prettier and better.

In the code above I am specifying the domain controller to query. That’s only helpful if I have an idea of the machine or site the user is trying to login to, and so know the domain controller responsible for that site. A better approach would be to query the domain controller with the PDC Emulator FSMO role for event ID 4740. This DC is the one that eventually processes the account lockout requests for all accounts in the domain.

The cmdlet Get-ADDomainContoller gives you the domain controller of the site you are in. Adding the switch -Filter * gives you all the domain controllers in the domain. And piping this through a where-object to filter out the domain controller with the “PDC Emulator” FSMO role gives you the domain controller we are interested in. Like thus:

The snippet above returns an object for the DC with the PDC Emulator role, to extract just the name do thus:

Next, let’s go about presenting the output above in a tidier format.

One thing to note is that all the info we want is in a property called Message.

So one option would be to do some fancy filtering using foreach-object like thus:

This will list out the multiple lockout entries with the timestamp on top in red.

A better option, however, is to use the Properties property, which is an array containing exactly the info we want as values.

Format-Table is your friend here:

A one line code that’s way better than opening Event Viewer and filtering through the logs manually.

And if you want to filter by the locked out status of a specific $user, pipe the output through a where-object like thus:

Format-Table FormatString

In the previous post I wanted to convert some output to percentage figures and resorted to type casting to get it done. Turns out I don’t need to do that. Was reading more about the Format-Table cmdlet and saw that the hash table that supplies calculated properties also supports a key called FormatString which let’s one specify the format of the output.

For a quick overview of FormatString read this MSDN page. You can see various format specifier strings mentioned there. These let you define the format of the output.

Say I want to see the current time and output the remaining minutes to the hour as a percentage.

I can get the current minute thus:

I can use an Expression block to show the balance minutes:

And if I want to format the output such that it’s shown as a percentage (the value multiplied by 100 with a % sign suffixed) I can use FormatString with the “P” format specifier:

This gives the output as a percentage figure with two decimal places (the default). I can modify that by suffixing a number to the “P” specifying the desired number of decimal places. Like for instance:

If I want to round, I can use the “r” specifier. I can do decimal, scientific, fixed point, and so on.

Returning to the code of the previous post, here’s a revised version:

There’s two changes: (1) I don’t type cast the Capacity to [uint32] any more to round it, rather I use the “F0” format specifier which stands for a fixed-point number with 0 decimal digits (i.e. a number like 42.9517669677734 gets converted to 43); (2) I don’t type cast for the free space to percentage either, I just use the “P1” format specifier which stands for a percentage figure with 1 decimal digit (i.e. a number like 48.6%).

Neat!

Get a list of machine names and logged on or active user(s)

The Win32_LoggedOnUser class gives a list of logged on users to a computer. It isn’t just users logged on interactively to the console or Remote Desktop (which is a good thing) but even users connected remotely via shared folders and such (not a good thing).

But just for the curious (and since I fiddled with it today) here’s how you can format the output of this class properly.

You can use PowerShell to get instances of this class via the Get-WMIObject cmdlet thus:

The output has a lot of material, so best to filter by the Antecedent property.

Instead of Format-Table you may use Select-Object or anything else you fancy.

To query remote computers using the -ComputerName switch.

The output isn’t precise. You get a list of local accounts as well, and there are repeat entries.

In my case I want to limit to the domain accounts. Also I want to skip accounts starting and ending with the “x” character (as these are my admin accounts) and any accounts entering with a “$” character (as these are machine accounts). And I want to eliminate the duplicates. So here’s what I did:

Once again, the beauty of PowerShell is that you can glue things together. So, for instance, if I want to get a list of machines in my domain along with the currently logged in users, I can do that.

To get a list of machines in a particular OU I use the following (this requires the ActiveDirectory module to be imported first!):

I already have the code for getting the logged in user on a machine, so all I need to do is pipe the output of the Get-ADComputer to Format-Table and tell it to populate the user column dynamically. Like thus:

The code within the Expression block is similar to the code for querying a single machine, except that I replace the machine name with $_.Name.

The output is a list of machines with the logged in user(s). If there are no logged in users that field is empty {} else it’s a single user name or a list of user names.

Of course, the Win32_LoggedOnUser class isn’t useful if users access each other’s machines via shared folders and such; but in that case it’s just a matter of replacing the Expression block above with code that gives more focussed results. Two great posts on how to list logged in users on a machine are this and this.

I wanted to expand the code above to also include IP addresses too. A PowerShell one-liner to get IP address from a name is the following:

To limit the output to just the IP address do the following:

And to restrict to just IPv4 addresses add the following:

Going by these it’s pretty obvious what we have to do to add an IP address column: