Musings of a PC

Thoughts about Windows, TV and technology in general

Category Archives: PowerShell

Easy unblocking of downloaded files in Windows

Windows tries to protect you from yourself, and it tries to do it without nannying you, but sometimes it can be a real nuisance. A good example is blocked files. If you download something from the Internet, it is considered to be “untrusted” and therefore blocked from being used until you go and unblock it.

Which is all well and good if it is just one file, but if you’ve downloaded a Zip file of source and you extract everything from the Zip file, you’ve then got to unblock each and every file.

Which is a nuisance if you are using Explorer, because you can only do one at a time.

So it was really nice to find that PowerShell can unblock files and you can provide it with a recursive listing of files to unblock, e.g.:

Get-ChildItem -Recurse | Unblock-File

USE WITH CAUTION! This will literally unblock every file from the current directory downwards, so be sure that you do trust everything you are unblocking, but it should save you some time.


My next self-tutorial on PowerShell

After a big, long break from blogging on PowerShell, I finally found the time and the incentive to do some more learning on how to write in PowerShell. The incentive was a combination of three things:
  • I’ve been trying to think of what to actually use PS for, and how to learn how to program in it. One of the ideas that has been going through my mind is taking some of the Script Centre scripts and re-writing them in PS.
  • A recent blog from Jeffrey Snover on setting the console title to be the current working directory. I’m glad he blogged about this ‘cos I saw his demos at Tech-Ed and I’d been wondering how he had done that neat trick.
  • A link from Jeffrey’s blog entry to a blog entry on the PowerShell for Fun site that goes into more detail on what you can do with setting the prompt. That entry ends with some suggestions on what the reader can do next …

So where did that leave me? I ended up wanting to write a prompt function that would warn me if the battery was running low. So, over to Script Centre first to see if I can find an existing script on getting battery information. Yep, there are a couple of scripts there, of which one ( deals with the Win32_Battery object.

I tried running that script on my laptop and it didn’t really give me much information that might have been useful. I checked MSDN about Win32_Battery but that didn’t really get me advanced on what I was trying to achieve. What I did find, though, was an article on using WMI with PowerShell, written by Andrew Barr at Microsoft:

This helps a lot. If you type get-wmiobject Win32_Battery then you get all of the properties that WMI can return from this. Looking through the list, I spotted EstimatedChargeRemaining … aha! I could write a prompt function that retrieves the estimated charge remaining and sets the prompt to be a different colour as the charge percentage goes down …

function prompt
   $charge = get-wmiobject Win32_Battery -property EstimatedChargeRemaining
   $colour = "White"
   if ($charge.EstimatedChargeRemaining -lt 50)
      $colour = "Yellow"
   if ($charge.EstimatedChargeRemaining -lt 25)
      $colour = "Red"
   Write-Host ("PS " + $(get-location) + ">") -nonewline -foregroundcolor $colour
   return " "

So if the battery charge drops below 50%, the prompt text should turn yellow. If it continues to fall and goes below 25%, it should go red.

A couple of things that caught me out briefly …

1. You must return something from the prompt function. In the example above, I’m returning a space. To begin with, I returned "" (i.e. an empty string) but PowerShell then issues the default prompt!

2. Being English, I accidentally started off trying to specify -foregroundcolour (i.e. with a u) … and then wondered why -foregroundcolour White was being issued as part of my prompt!

The PowerShell for Fun blog entry linked to above has a brief but useful reminder as to where PowerShell will gets its config files from when it starts up. If you want this function to be persistent, you need to add it to one of those files. The one complication here is that if you’ve only just started to use PowerShell, the script execution policy may be set to restricted. If that is the case then, when you start PowerShell having just added the prompt function to one of the startup files, you may see this error:

The file profile.ps1 cannot be loaded. The execution of scripts is disabled on this system. Please see "get-help about_signing" for more details.
At line:1 char:2
+ .  <<<< profile.ps1

If you type get-executionpolicy, you can find out what your machine is set to. Mine was set to restricted. I wanted to change it to RemoteSigned (so that local scripts can be run unsigned but downloaded ones must be signed) but my first attempt failed because I don’t have admin rights so the write to the registry failed.

Running PowerShell as an admin account and then typing set-executionpolicy RemoteSigned then allowed my format prompt to be set.

My first PowerShell script

Not one to try the eternal "Hello, World!" approach, I wanted to try to put PS to some practical use. As a fairly simple (!) start, I decided to take a fairly short VBScript I’d written that looks for empty groups and lists them. Here is the code:
  ‘ Define an ADO connection so that we can perform a SELECT against
  ‘ Active Directory
  Set objConnection = CreateObject("ADODB.Connection")
  Set objCommand =   CreateObject("ADODB.Command")
  objConnection.Provider = "ADsDSOObject"
  objConnection.Open "Active Directory Provider"
  Set objCommand.ActiveConnection = objConnection
  ‘ Set a couple of properties on the command so that it searches the
  ‘ way we want it to
  objCommand.Properties("Page Size") = 1000
  objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
  ‘ Specify the SELECT statement.
  objCommand.CommandText = _
    "SELECT Name,ADsPath FROM ‘LDAP://dc=contoso,dc=com’ WHERE objectCategory=’group’ ORDER BY Name" 
  Set objRecordSet = objCommand.Execute
  ‘ Make sure we are at the start of the result set
  ‘ Turn on error trapping
  On Error Resume Next
  ‘ Loop through the records in the result set
  Do Until objRecordSet.EOF
    ‘ Get the group object specified by the LDAP string in ADsPath
    Set objGroup = GetObject(objRecordSet.Fields("ADsPath"))
    ‘ Get the information for that object
    ‘ Make sure we clear any pre-existing error information
    ‘ because if the group is empty, the next line generates
    ‘ an error, which we then test against in order to find the
    ‘ empty groups. Nice!
    arrMemberOf = objGroup.GetEx("member")
    If Err.Number<>0 Then
      WScript.Echo objRecordSet.Fields("Name")
    End If
    ‘ Move on to the next record
    ‘ and loop
Hopefully the comments I’ve put in will explain how the VBScript works. If you need some more info about the ADO side of things, there is a great article in the Script Center:
In fact, they use an additional tweak that I haven’t used: setting the "Sort On" property, while I’ve added that to the SELECT statement.
Let’s now have a look at what I came up with in terms of PS code:
function Get-EmptyGroups()
   # Define an empty hash table of empty groups
   $egs = @()
   # Set up a new search
   $ds = new-object system.directoryservices.directorySearcher
   # Default page size is 1000
   # Default SearchRoot is the current domain
   # Specify the properties we want to retrieve
   # Sort on that property
   $ds.Sort.Propertyname = "name"
   # Limit it to just groups
   # and go do the search, putting the results into an object
   $groups = $ds.FindAll()
   # Now go through the groups, looking for empty ones
   foreach ($g in @($groups))
      $group = new-object DirectoryServices.DirectoryEntry($g.path)
      if (!$group.member)
         $egs += $g.get_Properties()[‘name’]
Again, hopefully the comments help with the understanding, but here are few key points that I picked up and are worth passing on in terms of how PS works and some of the language structures …
The reason for the hash table ($egs = @{}) is to store the results of the function. In this iteration of the code, I am just returning the group name so that the function does the same thing as the original VBScript code. However, if the line that reads
$egs += $g.get_Properties()[‘name’]
is changed to
$egs += $g
then the function will actually output an array of property values, namely the group name and its LDAP path. This can then easily be consumed by some more PS code in a useful manner.
We then replace 10 lines of VBScript with 5 lines of PS (ignoring comments). In PS, some properties get default values which helps significantly. For example, you’ll notice that I don’t need to know the domain I’m running under in order for the PS code to work. If you do want to get the domain and add some OUs, for example, under it, it is only a matter of a line or two to get that information (see for examples). The fact that PS allows you to directly work with the .NET framework delivers a level of power that, until now, meant you needed to work with a .NET language like C#. As we’ll see below, the fact that you can drive it interactively means that you can query the structures themselves to get information that Visual Studio sometimes struggles to deliver.
One difference between the two examples that might need explaining is that in the VBScript, I’m looking for objectCategory matching group, while in PS, I’m looking for objectClass matching group. I’m not an AD guru, so I don’t know if the difference is important . The reason why the PS code using objectClass is because that is what other sample scripts I found did.
Finally, we get to the foreach loop. This iterates through each of the entries in the returned array. It gets the group information using a DirectoryServices call. Note how I check for whether or not there are any members though! This is a big improvement over VBScript and one of the areas where the PS designers have gone to a lot of trouble to ensure consistency when the underlying OS might not do that!
So what is happening here? If there aren’t any members, $group.member is NULL – simple as that. No errors raised, no messy code, nothing. A simple if test.
Another little language point: $g contains an array of properties, called ‘name’ and ‘adspath’. The line that starts $egs += shows how you extract a particular index from the array. Note that the property names are case sensitive. If you don’t match the string exactly, you don’t get anything back, including an error.
Another little tip: one of the really fantastic and powerful features of PS is that you can drive it interactively. This is a very very useful way for building up your code and testing it as you go. Try doing that with any of the other programming languages that MS provide!
If you want to try this for yourself, copy and paste the function a line at a time, starting from the line that starts $ds =  and finish with $groups = $ds.FindAll(). At this point, $groups will contain all of the groups in your AD domain. Now type this:
$g = $groups[1]
This will get the second entry in the array. Yes, PS counts like C does – it starts arrays at zero.
Now type this:
You should see something like this:
Path                                    Properties
—-                                    ———-
LDAP://CN=Administrators,CN=Builtin,… {name, adspath}
Don’t worry if the LDAP path doesn’t match. So what is this output telling us? That $g is an object with a Path value and a set of Properties. What can we do with this object? Try this:
$g | get-member
You should now see this:
   TypeName: System.DirectoryServices.SearchResult
Name              MemberType Definition
—-              ———- ———-
Equals            Method     System.Boolean Equals(Object obj)
get_Path          Method     System.String get_Path()
get_Properties    Method     System.DirectoryServices.ResultPropertyCollecti…
GetDirectoryEntry Method     System.DirectoryServices.DirectoryEntry GetDire…
GetHashCode       Method     System.Int32 GetHashCode()
GetType           Method     System.Type GetType()
ToString          Method     System.String ToString()
Path              Property   System.String Path {get;}
Properties        Property   System.DirectoryServices.ResultPropertyCollecti…
Note the very first line – the type name. PS is very good on types. This is helpful to us as newbies because it means that if you need more information than is given here, you can go to MSDN and do a search for System.DirectoryServices.SearchResult and get lots more juicy info about directory searches:
Finally, the $egs at the end of the function simply outputs the results. If you call the function interactively, this will be output on the console, otherwise you can pipe it to another cmdlet.
Wow – quite a long post! There was a lot learnt in this example. There are some questions in there (like what is the difference between objectCategory and objectClass). There are also some next steps for me to take like looking at parameters to allow me to specify whether I want the group name or the group attributes returned.
Until next time …

PowerShell Resources

This is a list of various resources that I’ve found useful. It is my intention to update this list over time, adding more links as I come across them. Feel free to add comments with your own favourites.

Sorting help from PowerShell

I thought I’d start by sharing a question I had, and the answer I received from kind contributors to the Microsoft newsgroup If you haven’t started looking at this newsgroup, I would certainly recommend it. Even if things go over your head initially, it is useful to try to read at least some of the postings in order to try to see what other people are doing with PS.
So: what was my question?
Well, PowerShell has a very powerful help system. During my learning period, I’m relying heavily on it to help me try to figure out how to achieve what I want. The help system supports wildcards and there is a strong naming convention in PS. You can combine both features to help you guess at what you are looking for.
For example, if you want to get something, the likelihood is that the command starts with Get-. So what commands are there?
Name                       Category                   Synopsis
—-                       ——–                   ——–
Get-Command                Command                    Retrieves basic inform…
Get-Help                   Command                    Opens the help files
Get-History                Command                    Gets a listing of the …
Get-PSSnapin               Command                    Lists registered PSSna…
Get-EventLog               Command                    Gets eventlog data for…
Get-ChildItem              Command                    Retrieves the child it…
Get-Content                Command                    The get-content comman…
Get-ItemProperty           Command                    Retrieves the properti…
Get-WmiObject              Command                    Produce a WMI Object o…
Get-Location               Command                    Displays the current l…
Get-PSDrive                Command                    Gets the drive informa…
Get-Item                   Command                    Returns an object that…
Get-PSProvider             Command                    Gets information for t…
Get-Process                Command                    Gets a list of process…
Get-Service                Command                    Gets a list of services.
Get-Acl                    Command                    Gets the access contro…
Get-PfxCertificate         Command                    Gets the pfx certifica…
Get-Credential             Command                    Gets a credential obje…
Get-ExecutionPolicy        Command                    Gets the effective exe…
Get-AuthenticodeSignature  Command                    Gets the signature obj…
Get-Alias                  Command                    Returns alias names fo…
Get-Culture                Command                    Gets the culture infor…
Get-Date                   Command                    Gets current date and …
Get-Host                   Command                    Gets host information
Get-Member                 Command                    This Cmdlet enumerates…
Get-UICulture              Command                    Gets the uiculture inf…
Get-Unique                 Command                    Gets the unique items …
Get-Variable               Command                    Gets a Ps variable
Get-TraceSource            Command                    Lists properties for g…
OK – but that list isn’t in alphabetical order. How about piping it through sort?
help get-* | sort
Name                       Category                   Synopsis
—-                       ——–                   ——–
Get-Command                Command                    Retrieves basic inform…
Get-Help                   Command                    Opens the help files
Get-History                Command                    Gets a listing of the …
Get-PSSnapin               Command                    Lists registered PSSna…
Get-EventLog               Command                    Gets eventlog data for…
Get-ChildItem              Command                    Retrieves the child it…
Get-Content                Command                    The get-content comman…
… and so on. Well, that didn’t help, so what is going on here? Well, it turns out that the help command is actually a built-in function that does this:
Get-Help $help | Out-Host -paging
It takes the parameter you provide, passes it to Get-Help and then pipes it through to the paging system. This means that if you then try to pipe it to sort, sort doesn’t see anything useful to sort on.
The solution, therefore, is to do this:
get-help get-* | sort -property name | Out-Host -paging
One of my next challenges will be to see how to create a get-help-sorted cmdlet with an alias.

Getting started with PowerShell

Encouraged by the two sessions at Tech-Ed that I attended given by Jeffrey Snover on PowerShell, I’m going to make a concerted effort to get to grips with the new environment. As I think I’m going to find the learning curve quite steep, initially at least, I’m going to do my best to blog about my experiences so that other newbies can benefit from the hurdles I’ve jumped over. As a result, I’ve created a new category for these posts.
I don’t know yet how regularly I’ll be blogging about PS – it will mainly be dependent on workload and what ideas I’ve got to try out on PS.