Pages

Tuesday, March 11, 2014

Get-Member Methods, Properties, and Garbage

Get-Member. As you may or may not know (probably not if you are reading this blog), Powershell is an object-oriented language. In this sense, we work with cmdlets, which contains objects, wether it be a method, or a property, or a set of properties. Since here on Powershell Pancakes I only believe in the lightest of rubbish and nonsense, I have decided to go over this very briefly, and how it should make your life immensely easier (theoretically).

Every cmdlet in Powershell has a vast group of members associated with it, a lot of these are standard across all objects, especially when it concerns strings. For the experienced, usually we are aware of how to compare strings and the various operators associated with doing such tasks. But for objects that contain useful stuff, such as Get-Process, any of the WMI classes, objects returned from Active Directory - how would you know, exactly what kind of properties and methods you can perform related to that object? Unless you are like a close friend of mine, who attempted to memorize a book of part numbers for ball bearings (he failed, by the way), you wouldn't possibly be able to recollect everything about every object. Seldom do we write scripts each and every day that touch on all of our knowledge of everything we know about Powershell.

So let's get down to it, then;

Pancake:
Get-Process | gm

Two things to note - GM is an alias for Get-Member (who would have guessed?), and you need to pipe Get-Member after a cmdlet to retrieve it's properties and methods. Some cmdlets operate with a -Properties switch, which you will usually find on cmdlets from the VIM modules or from the Active Directory modules, as well as others for Exchange and what not. The ISE will help you out in determining what switches are available to you in each cmdlet. Also your trusty Clippy equivalent is Get-Help

For our Get-Process example above, it returned quite a large number of members. But how do we actually use these? The syntax below is incorrect;

Get-Process | Get-Member mainmodule

With that, you are just going to retrieve what that member actually is, not what it contains. As you see, MainModule is a MemberType that is a Property. To utilize this, try the below;

Get-Process | select MainModule

Now we have just listed the executables associated with a selection of processes. Let's try another one, referring to our Get-Process | gm list of members.

Get-Process | select processname,mainmodule

Aha! Now we are getting somewhere, arn't we? It should be becoming apparant, that to select the Property of an object, you need to pipe | Select Property1,Property2,... and so on. As many properties as you want. In the above example, we found out the process name as listed in the services mmc.

Ah yes but wait, when I ran Get-Process | Get-Member, I saw a lot of other rubbish as well, like - a method?! Yes. A member does not mean it is a Property, a member also denotes Methods, which can be used to work with objects in that cmdlet. Let's look at the rather conspicious names of some of the methods associated with Get-Process. We have, Kill, Refresh, Start, Close, and a bunch of other ones I don't really know how to use. But you use them all in essentially the same way. Let's give you an example, using Kill.

Get-Process | Where {$_.ProcessName -like "*powershell*"} | kill

Oh bugger, you just closed your Powershell window. But do you see? Using the Where {$_. } filter, we were able to select a process based on it's ProcessName. You can use any of the properties available to you to search for a process, and you can find (here it comes) all of those properties available to you by using Get-Member. Though you can use it in other ways too, you don't need to just kill things. Maybe I just want to get the process ID for anything with a process name of Powershell;

Get-Process | Where {$_.ProcessName -like "*Powershell*"} | Select ID

Hopefully this is rather apparant now, on how to better utilize different membertypes.

Saturday, February 15, 2014

Some useful notes

I've come accross a few oddities recently on jobs, passing variables to them, along with Invoke-Expression. As well as a new tip on throttling your jobs.

I was unsatisfied that my jobs were being removed as a form of throttle control. If we recall from an earlier blog post I had done here, we were looking at the following bit of code to control how many jobs we were running at the same time;

DO { 
Remove-Job -State Completed
} until (@(Get-Job).Count -le 25)

This of course works, however if you are running 50 jobs, when you do Get-Job after they have all kicked off, you're going to be left with information on only 25 jobs. Fortunately, this is as easy to fix as a hot pocket. All we need to do is change it to the following;

DO { 
Start-Sleep -Seconds 1
} until (@(Get-Job -State Running).Count -le 25)

This lets us halt the creation of new jobs as long as the running job count is 25. This is a much better solution, as we can still do Receive-Job on all of our prior jobs, and none of them get removed from our console.

Another oddity I came across was variables taking the place of other variables in -ArgumentList. There is no easy way to explain it, however I noticed my variables in my jobs were using other variables, or submitting a null value. After using Write-Host way too many times, I found that some of my variables were not being passed correctly to my job, or one variable, was showing the value of another variable! How odd is that? It came down to passing variables that didn't exist in the job, or missing a variable that you have forgotten to pass. In this case things get all screwey, and your variables are either blank, or using values from other variables, depending on their placement in -ArgumentList. If in doubt, using Write-Host and then using Receive-Job to see what values your variables have, is a good way to troubleshoot and issue like this.

I found this quite odd and this is the first time I've run into it.

Sunday, January 26, 2014

Creating output with objects

Reporting, ah yes. I know I covered this earlier, and in great detail, but actually I only detailed one real method of outputting useful reports. I want to cover something else, kind of like creating a here statement, except with a few special abilities. Your boss would never know if you did any work if it wasn't for the ability to run reports on things. So in our endless pursuit of trying to make scripts that output simpler, better, more attractive reports - this battle is endless.

So let's get right down to it. We've already got the points of turning objects into variables, and in general I think we are a little over the 'what does the $ mean?' phase. Basically what we are doing is using New-Object, to create a new Powershell Object (PSObject), and then we are going to add members to that object. Kind of like building your own object with properties, except you define the properties and what is contained in those fields. We will use the object we create to output to CSV.

Pancake:
$TestObj = New-Object PSObject

Alright, now we have an object, and empty one, but still, we have an object. We need to put stuff into this object. So what do we do? Well, let's use my favorite command, Get-WMIObject Win32_BIOS. I bet you didn't see that coming. So let's use our common need, loop through a file of computers, get the BIOS info, put it into a nice looking spreadsheet. What we have to do to accomplish this is add members to the object. As an example, if you type out Get-WMIObject Win32_BIOS, you are going to get a bunch of values associated with this object. In your prior experience, you can use the values of this object to put it into some form of output, or grab just the particular part that you need. We are doing the same thing with our very own object, except we determine what fields are in the object, and what are in those fields. Usually gathered from other sources. This may make it a bit easier to think about. So let's add a member to our virgin object.

$TestObj = New-Object PSOBject
$TestObj | Add-Member -MemberType NoteProperty -Name "Name" -Value "SmurfCakes"
$TestObj | Add-Member -MemberType NoteProperty -Name "Pancake Flavor" -Value "Blueberries"
$TestObj

So now we have two fields, populated with our values, which I have defined. They are static values though. Of course, we can put in variables read from a whole list of pancake flavors (or serial numbers..whatever). In this case, the name of the Field is "Name", and "Pancake Flavor", with the values SmurfCakes for a name, and Blueberries for the flavor profile. -MemberType defines exactly what you are adding, in this case we are adding a NoteProperty. There are many types of members, you can specify objects as Methods, Properties, ScriptMethods, etc. However for this case, we are creating a NoteProperty. In this instance, the NoteProperty -Name "" is the name of our column, while the Value is what goes in that column.

Let's get some information from the BIOS and make some nice output.
$GetBIOS = Get-WmiObject Win32_BIOS

#Create your object
$BIOSInfo = New-Object PSObject

#Get your values
$BIOSManufacturer = $GetBIOS.Manufacturer
$BIOSSerial = $GetBIOS.SerialNumber
$BIOSVersion = $GetBIOS.SMBIOSBIOSVersion

#Add your properties
$BIOSInfo | Add-Member -MemberType NoteProperty -Name "Manufacturer" -Value "$BIOSManufacturer" | Select -ExpandProperty Manufacturer
$BIOSInfo | Add-Member -MemberType NoteProperty -Name "Serial Number" -Value "$BIOSSerial" | Select -ExpandProperty SerialNumber
$BIOSInfo | Add-Member -MemberType NoteProperty -Name "SMB Version" -Value "$BIOSVersion" | Select -ExpandProperty SMBIOSBIOSVersion
#Export
$BIOSInfo | Export-CSV C:\Working\BIOSOutput.csv -NoTypeInformation
Nothing too crazy here, we are getting our BIOS info from $GetBIOS, and then we are selecting the properties we want and putting them into variables, and then we are adding them to our $BIOSInfo object. Finally after all of that, we do an Export-CSV to get our nice, pretty output. Note that this example was built with the purpose of leveraging this capacity, usually in your scripts you won't need to select all your variables like we had to do above, since you are probably reading them from a file, combined with some properties that you will need to select.

Of course, we could simply loop this script over a ton of machines, and add our computername to identify each one in the table.
$ComputerList = Get-Content C:\Working\Computerlist.txt

foreach ($Computer in $ComputerList)
{
$GetBIOS = Get-WmiObject Win32_BIOS

#Create your object
$BIOSInfo = New-Object PSObject

#Get your values
$BIOSManufacturer = $GetBIOS.Manufacturer
$BIOSSerial = $GetBIOS.SerialNumber
$BIOSVersion = $GetBIOS.SMBIOSBIOSVersion

#Add your properties
$BIOSInfo | Add-Member -MemberType NoteProperty -Name "Computer Name" -Value "$Computer"
$BIOSInfo | Add-Member -MemberType NoteProperty -Name "Manufacturer" -Value "$BIOSManufacturer" | Select -ExpandProperty Manufacturer
$BIOSInfo | Add-Member -MemberType NoteProperty -Name "Serial Number" -Value "$BIOSSerial" | Select -ExpandProperty SerialNumber
$BIOSInfo | Add-Member -MemberType NoteProperty -Name "SMB Version" -Value "$BIOSVersion" | Select -ExpandProperty SMBIOSBIOSVersion
#Export
$BIOSInfo | Export-CSV C:\Working\BIOSOutput.csv -NoClobber -NoTypeInformation
}
If you noticed we don't need to pipe a Select -ExpandProperty for the value $Computer, that is because it is already and isolated value and not an object container on its own.

I think that is about all for writing your own custom objects to CSV and such. A fairly easy and super useful tool in your PowershellPancake kitchen.