Quantcast
Channel: Hey, Scripting Guy! Blog
Viewing all 3333 articles
Browse latest View live

Add, Modify, Verify, and Sort Your PowerShell Array

$
0
0

Summary: Learn how to add, modify, or verify values in a Windows PowerShell array, and discover two easy techniques for sorting your array.

Hey, Scripting Guy! Question Hey, Scripting Guy! I get that arrays are really simple in Windows PowerShell, but it seems that they are so simple that no one ever seems to tell us how to work with them. For example, I need to know how to add items to an array or how to change an item that is in an array. I have searched all over the Internet, and I cannot seem to find a straight answer. I know you guys seem to love hash tables, but that seems to be a lot of overhead for a simple array. Can you help?

—PT

Hey, Scripting Guy! Answer Hello PT,

Microsoft Scripting Guy, Ed Wilson, is here. You are right. You are also right, and unfortunately, you are right—but I can help solve that problem. OK, maybe I had better explain a bit. You are right, arrays in Windows PowerShell are simple, and in fact, they are so simple that many people do not bother to talk about how simple they are. It is also true that Windows PowerShell loves hash tables, but the same is true for arrays. In fact, Windows PowerShell loves arrays so much that they are incredibly easy to use.

Note: This is part two of a multiple blog series about working with arrays and hash tables for data storage. In yesterday’s Hey, Scripting Guy! Blog, Learn Simple Ways to Handle Windows PowerShell Arrays, I discussed creating arrays, indexing into arrays, and two techniques for walking through an array.

Working with specific array elements

One of the interesting things about arrays in Windows PowerShell is they are able to hold different data types. For example, I can store numbers and strings in the same array as shown here.

PS C:\> $a = 1,2,3,"four"

PS C:\> $a

1

2

3

Four

Changing element values

If I need to change an element in array, I can index into the array by using the square bracket and the index number. To find the upper boundary, I use the GetUpperBound method, and when I have that value, I can easily find the element I need to modify. This technique is shown here.

PS C:\> $a.GetUpperBound(0)

3

PS C:\> $a[3] = 4

PS C:\> $a

1

2

3

4

Adding a new element to an existing array

If I want to add an element to an existing array, it might make sense to choose the next index number, and attempt to assign a value in the same way that I change an existing value. When I do this, however, Windows PowerShell generates an out of range error message. This command and error message are shown here.

PS C:\> $a[4] = 12

Array assignment failed because index '4' was out of range.

At line:1 char:4

+ $a[ <<<< 4] = 12

    + CategoryInfo          : InvalidOperation: (4:Int32) [], RuntimeException

    + FullyQualifiedErrorId : IndexOutOfRange

The way to add a new element to an existing array is to use the += operator as shown here.

$a += 12

The commands to create an array, get the upper boundary of an array, change an element in an array, and add a new element to an array are shown here with their associated output.

Image of script

Searching for a specific value in an array

One question that comes up from time-to-time is, “How do I know whether a value is contained in an array?” The answer is, once again, rather easy, “Use the Contains operator”. The following two commands use the previously created $a array. In the first command, the number 12 is present, and the value True returns. In the second example, the array does not contain the number 14; and therefore, the returned value is False.

PS C:\> $a -contains 12

True

PS C:\> $a -contains 14

False

PS C:\>

Sorting an array

Now suppose I need to sort my array. There are actually two ways to do this. The first way to do this is to use the Sort-Object cmdlet (Sort is an alias for the Sort-Object cmdlet). The second way to sort an array is to use the static Sort method from the System.Array .NET Framework class.

Use Sort and the pipeline

The first technique I will discuss is also the easiest to use. It is the pipeline method. All that this technique requires is to pipe the array to the cmdlet. This technique is shown here.

PS C:\> [int[]]$a = 1,5,7,2,12,4

PS C:\> $a | Sort-Object

1

2

4

5

7

12

The thing to keep in mind is that this does not change the array, it merely changes the display output. If I want to modify the actual array, I need to write the results back to the original array. This technique is shown here.

PS C:\> $a = $a | sort

PS C:\> $a

1

2

4

5

7

12

The commands to create an array of integers, sort the results with the Sort-Object cmdlet, and write the results back to the array are shown in the following image.

Image of script

Use Get-Random to create a random array

One of my favorite tricks is to create an array of numbers by using the Get-Random cmdlet. To do this, I use Count to specify the number of values to select, and I use a range operator to create an array of numbers from which to choose. I then write the random values to a variable. This technique is shown here.

$rnd = Get-Random -Count 10 -InputObject (1..100000)

Use the static Sort method

To sort the random numbers, I use the Sort static method from the System.Array .NET Framework class. Because Sort is a static method, I need to use a double colon separator between the class name (in square brackets) and the method name. I supply the array of random numbers as an input value. This command is shown here.

[array]::sort($rnd)

What is really interesting is that the Sort static method, automatically writes the sorted values back to the array that is contained in the $rnd variable. The commands to create a random array of numbers, display those values, sort the array, and display the sorted list are shown in the following image.

Image of script

Measuring the difference in performance

“So, what is the difference between the two ways to sort arrays,” you may ask. The difference is that the pipeline way of sorting is probably more intuitive to Windows PowerShell users. The other difference is that the static Sort method from the System.Array class is way faster. To check this, I like to use the Measure-Command cmdlet. To ensure I have a large enough data set that will take a decent amount of time to sort, I create two random arrays by using the commands that are shown here.

$arrayA = Get-Random -Count 1000000 -InputObject (1..1000000)

$arrayB = Get-Random -Count 1000000 -InputObject (1..1000000)

Next, I use the Measure-Command cmdlet to measure the two ways of sorting arrays. The two commands are shown here.

Measure-Command -Expression { $arrayA = $arrayA | Sort-Object }

Measure-Command -Expression { [array]::Sort($arrayB) }

On my system (which is a fast computer), the first command takes a little over 40 seconds, whereas the second command takes a little more than 8 seconds. Therefore, the second command appears to be five times faster than the first command that uses the pipeline.

The commands that create the two random arrays use Measure-Command to check the speed of the two commands, and they display the first two numbers in each of the newly sorted arrays, as shown in the following image.

Image of script

Sorting arrays that contain multiple types

There is one more caveat when it comes to using the two sort methods. Because an array in Windows PowerShell can contain different types, the Sort-Object method may be the preferred way of sorting objects. This is because in using the default comparer, the Sort static method fails when the array contains different types.

In the following example, I create an array that contains both integers and strings. I then pipe the array to the Sort-Object cmdlet (using Sort as the alias). Next, I attempt to use the static Sort method from the System.Array class, and that generates an error message.

PS C:\> $array = 1,2,9,8,3,"four","tree","Cat","bat"

PS C:\> $array | sort

1

2

3

8

9

bat

Cat

four

tree

PS C:\> [array]::sort($array)

Exception calling "Sort" with "1" argument(s): "Failed to compare two elements in the array."

At line:1 char:14

+ [array]::sort <<<< ($array)

    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException

    + FullyQualifiedErrorId : DotNetMethodException

 

PS C:\>

Because an array can contain other objects (besides strings and integers), I decide to perform one additional test, and I therefore store an instance of the System.Diagnostics.Process .NET Framework class in the last element. This command is shown here.

PS C:\> $array = 1,2,9,8,3,"four","tree","Cat","bat",(get-process winword)

PS C:\> $array

1

2

9

8

3

four

tree

Cat

bat

 

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName

-------  ------    -----      ----- -----   ------     -- -----------

    463      62    41772      89736   369    39.17   4460 WINWORD

Next, I decide to sort the array. Once again, the Sort-Object cmdlet comes through with no problems. This output is shown here.

PS C:\> $array | sort

1

2

3

8

9

bat

Cat

four

 

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName

-------  ------    -----      ----- -----   ------     -- -----------

    463      62    41772      89736   369    39.17   4460 WINWORD

tree

 

 

PS C:\>

 

PT, that is all there is to modifying values in an array, adding to an array, checking to see if an array contains a specific value, and sorting the array. Array Week will continue tomorrow when I will store different types of objects in an array (including other arrays). It will be fun and educational. See ya!

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 


Find the Index Number of a Value in a PowerShell Array

$
0
0

Summary: In this blog post, Microsoft Scripting Guy, Ed Wilson, talks about finding the index number of a value in a Windows PowerShell array.

Microsoft Scripting Guy, Ed Wilson, is here. The other day, the Scripting Wife and I were invited to speak with a customer in Charlotte. Our contact, Rafael, is a really cool guy (he is also a SQL Server MVP). We met Rafael at various SQL Saturday events in the southern portion of the United States. It was great to see our friend again, and the group was extremely engaged and asked lots of great questions. One question I received had to do with arrays, namely, “How can I determine the element number of an array to see the data in that array?”

Note: This is the third blog post in a series devoted to working with arrays in Windows PowerShell. In the first post, Learn Simple Ways to Handle Windows PowerShell Arrays, I discussed creating arrays, indexing into arrays, and two techniques for walking through an array. In yesterday’s post, Add, Modify, Verify, and Sort Your PowerShell Array, I discussed adding, modifying, or verifying values in a Windows PowerShell array, and two easy techniques for sorting the array.

Find the index of value in an array

The first thing I need to do is create an array that contains a number of random numbers. (This is not a requirement to find an index value in an array, but it is a requirement for the demo. Also, being able to create a random array like this is useful when it comes to practicing with arrays.) I like to use the Get-Random cmdlet to do this because it is easier than doing a bunch of typing. Here is the command to create an array that contains 10 random numbers.

$array = Get-Random -Count 10 -in(1..100)

Use the for statement

Now, I use a for statement with a pipeline that begins at zero, a test pipeline that continues until i is 1 less than the length of the array, and an increment pipeline that increases the value of i by 1. Here is the for condition.

for($i=0;$i-le $array.length-1;$i++)

The script block that is associated with the for statement uses parameter substitution and the format operator to display the counter ($i) value and the value that is contained in the specific array element. Here is the script block portion of the for statement.

{"`$array[{0}] = {1}" -f $i,$array[$i]}

The command to create an array of random numbers and display the array element numbers in addition to the values that are contained in the array element are shown in the following image.

Image of script

Using the indexof static method

The System.Array .NET Framework class contains a number of static methods that are useful. These static methods are shown here (they are all well documented on MSDN).

PS C:\> [array] | gm -s | select name, definition | ft -a

 

Name            Definition

----            ----------

AsReadOnly      static System.Collections.ObjectModel.ReadOnlyCollection[T] AsRea...

BinarySearch    static int BinarySearch(array array, System.Object value), static...

Clear           static System.Void Clear(array array, int index, int length)

ConstrainedCopy static System.Void ConstrainedCopy(array sourceArray, int sourceI...

ConvertAll      static TOutput[] ConvertAll[TInput, TOutput](TInput[] array, Syst...

Copy            static System.Void Copy(array sourceArray, array destinationArray...

CreateInstance  static array CreateInstance(type elementType, int length), static...

Equals          static bool Equals(System.Object objA, System.Object objB)

Exists          static bool Exists[T](T[] array, System.Predicate[T] match)

Find            static T Find[T](T[] array, System.Predicate[T] match)

FindAll         static T[] FindAll[T](T[] array, System.Predicate[T] match)

FindIndex       static int FindIndex[T](T[] array, System.Predicate[T] match), st...

FindLast        static T FindLast[T](T[] array, System.Predicate[T] match)

FindLastIndex   static int FindLastIndex[T](T[] array, System.Predicate[T] match)...

ForEach         static System.Void ForEach[T](T[] array, System.Action[T] action)

IndexOf         static int IndexOf(array array, System.Object value), static int ...

LastIndexOf     static int LastIndexOf(array array, System.Object value), static ...

ReferenceEquals static bool ReferenceEquals(System.Object objA, System.Object objB)

Resize          static System.Void Resize[T](T[]& array, int newSize)

Reverse         static System.Void Reverse(array array), static System.Void Rever...

Sort            static System.Void Sort(array array), static System.Void Sort(arr...

TrueForAll      static bool TrueForAll[T](T[] array, System.Predicate[T] match)

 

To use the indexof static method, I provide an array and a value that I want to find. This is shown here.

[array]::indexof($array,39)

The command to create an array of 10 random numbers, display the contents of the array, find the index number of one item in the array, and then verify that value is shown in the following image.

Image of script

Work with one half of the array

It is common to need to work with one half of an array at a time. As it turns out, this is incredibly easy to do—I simply use the range operator. To test this, I first want to create an array with 20 elements in it. To do this, I use the range operator and store the array of numbers in a variable. This command is shown here.

$array = 1..20

Next, I test to ensure that I have 20 elements in my array. I do this by displaying the content as shown here.

PS C:\> $array

1

2

<output truncated>

20

Instead of displaying the content of a rather large array, I could use the Count or the Length properties as shown here.

PS C:\> $array.count

20

PS C:\> $array.Length

20

I now use the range operator and the GetUpperBound method to display the first half of the array. This command and its associated output are shown here.

PS C:\> $array[0..($array.GetUpperBound(0)/2)]

1

2

3

4

5

6

7

8

9

10

11

Well, the previous command works alright, but the problem seems to be that it displayed one additional element: –element 11. To fix this requires subtracting one from the upper boundary of the array. Here is the revised code and its output.

PS C:\> $array[0..(($array.GetUpperBound(0)/2)-1)]

1

2

3

4

5

6

7

8

9

Bummer! That did not work as I had expected. I then decide to examine the value with which I am working. Here is the result.

PS C:\> ($array.GetUpperBound(0)/2)

9.5

How did I get 9.5 when I have 20 elements in my array? Well, this is because the upper boundary of the array is 19 (remember we start counting at 0). I then decide to “cheat” and convert the value to an integer. The revised command and output are shown here.

PS C:\> $array[0..([int]($array.GetUpperBound(0)/2)-1)]

1

2

3

4

5

6

7

8

9

10

With this in mind, I can now access the second half of the array. Here is my command to accomplish that task.

PS C:\> $array[[int]($array.GetUpperBound(0)/2)..($array.GetUpperBound(0))]

11

12

13

14

15

16

17

18

19

20

That is all there is to finding a value in an array when you do not know the index number. Array Week will continue tomorrow when I will talk about working with arrays of arrays.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Read a CSV File and Build Distinguished Names on the Fly by Using PowerShell

$
0
0

Summary: Learn two ways to read a CSV file with Windows PowerShell and build distinguished names on the fly.

Hey, Scripting Guy! Question Hey, Scripting Guy! I am working on my first big project with Windows PowerShell, and I have been unable to locate a good answer on the Internet. I am having trouble figuring out how to connect a bunch of different array items. Here is a quick look at my code:

$data = “c:\fso\myusers.csv”

$list = import-csv $data | select class, dept, campus –unique

After I import the CSV file and chose the properties I need, I attempt to create a distinguished name attribute from the data. Here is what that code looks like.

$dn = “OU=$list[0].class,OU=$list[0].dept,OU=$list[0].campus,DC=contoso,DC=com”

Unfortunately, this command does not work. I ended up having to put each piece of the array into its own variable before I can get it to go together properly. Here is what I came up with.

$part1 = $list[0].class

$part2 = $list[0].dept

$part3 = $list[0].campus

$dn = “OU=$part1,OU=$part2,OU=$part3,DC=contoso,DC=com”

I am not certain why my first attempt failed, nor am I certain if my second attempt is the best approach. I am hoping that you can take what I have given you and come up with a better way of doing things. I love your funny way of writing—it makes me laugh most of the time (when I am not pulling my hair out)…LOL. Thank you for what you do.

—TJ

Hey, Scripting Guy! Answer Hello TJ,

Microsoft Scripting Guy, Ed Wilson, is here. I am glad that you enjoy the blog posts, and I am sorry that you are having a bit of difficulty with the array.

Note: This is the fourth post in a series of blog posts that are devoted to discussing working with arrays in Windows PowerShell.

First test reading a CSV file

TJ, the first thing I need to do is to create a CSV file that mimics your setup. I created a file named myusers.csv, and I placed it in my c:\fso folder. The CSV file contains Class, Dept, and Campus column headings. The CSV file is shown in the following image.

Image of file

After I have the CSV file created, I use the Data variable to store the path, and I call the Import-CSV cmdlet to read the content of the file into another variable named List. I then display the content of the variable to ensure that the file parses correctly. These three commands are shown here.

$data = "C:\fso\myusers.csv"

$list = Import-Csv $data

$list

TJ, now it is time to see the problem you are experiencing. Here is the command you use.

“OU=$list[0].class,OU=$list[0].dept,OU=$list[0].campus,DC=contoso,DC=com”

The command looks like it should work. You index into the data that is stored in the List variable, and you use dotted notation to retrieve each portion you require. When I run the command, the string literals appear, but the values from the CSV do not populate. The commands that import the CSV file and verify the content of the file, in addition to the command that does not work properly and its associated output are shown in the following image.

Image of command output

The problem is that the data from the CSV file is not displaying in the output. But as shown here, the data does in fact exist because it displays when I use dotted notation to retrieve the data from the first element in the array.

PS C:\> $list[0].class

101

PS C:\> $list[0].dept

math

PS C:\> $list[0].campus

main

I need to force the evaluation of each value so I can display the data. To do this, I use a subexpression. A subexpression consists of a dollar sign and a pair of parentheses. Here is the revised command that will display the data properly.

“OU=$($list[0].class),OU=$($list[0].dept),OU=$($list[0].campus),DC=contoso,DC=com”

The command that creates the CSV file, uses dotted notation to retrieve column data, and incorporates a subexpression around each substitution group, and its associated output are shown in the following image.

Image of command output

Although the previous command works, the code is a bit complex and cluttered. It makes it difficult to see clearly what is actually going on. A better way to do this is to use parameter substitution. To do this, each parameter that requires a substitution appears in a pair of curly brackets with a number. The f operator appears on the right side of the substitution, and each replacement value appears on the right side in a comma-separated fashion. Here is the revised code.

“OU={0},OU={1},OU={2},DC=contoso,DC=com” -f $list[0].class,$list[0].dept,$list[0].campus

The code that creates the CSV file and uses parameter substitution to replace the tokens in the output is shown in the following image.

Image of command output

Now that we understand the fundamentals of using a parameter substitution technique, let’s see how to apply this to each item in the CSV file to create a collection of distinguished names. I can accomplish this in a single line of code. I decided to drop the $data variable, and I assign the path directly to the CSV file to the Import-CSV cmdlet. I pipe the results to the Foreach-Object cmdlet, and inside the associated script block, I do the parameter substitution.

The change to the original code is to use the $_ automatic variable, instead of indexing into the array. The $_  automatic variable refers to the current item on the pipeline, and it takes the place of the array index number in the code. (I could shorten the command by using the alias ipcsv for Import-CSV and by using % for the Foreach-Object cmdlet). Here is the revised line of code. (The following is a single-line command, but I broke the command at the pipe character due to display constraints).

import-csv c:\fso\myusers.csv |

foreach-object {“OU={0},OU={1},OU={2},DC=contoso,DC=com” -f $_.class,$_.dept,$_.campus}

The following image displays the one-line command to read a CSV file, and it uses parameter substitution to create the distinguished names.

Image of command output

TJ, that is all there is to reading a CSV file and retrieving items from the array to make a substitution. Array Week will continue tomorrow when I will talk about multidimensional arrays and adding to arrays.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Easily Create and Manipulate an Array of Arrays in PowerShell

$
0
0

Summary: Learn about creating an array of arrays in Windows PowerShell, and see how to store numerical data and rich objects in elements.

Microsoft Scripting Guy, Ed Wilson, is here. Yesterday in Charlotte, North Carolina in the United States, it was 60 degrees Fahrenheit (that is 15 degrees Celsius according to my unit conversion module). I was bragging about this to my friend, George, who lives in Ottawa, Canada. Today it is 32 degrees Fahrenheit (0 degrees Celsius—I don’t need a unit conversion module to do that one), and there is frost on the ground. I guess that is what I get for bragging.

Anyway, the Scripting Wife and I are back in Charlotte, after our quick trip to Phoenix, Arizona to speak at the Arizona PowerShell Users Group meeting. The event was great, and they are the perfect hosts. In fact, we are planning an International PowerShell Users Group Day in conjunction with them, which should happen in the spring of 2012. Stay tuned for more information on this exciting event.

Note: This is the fifth post in a series of blog posts that are devoted to discussing working with arrays in Windows PowerShell.

Creating an array of arrays

One of the cool things to do with an array is to create an array that contains other arrays. In Windows PowerShell this is super easy. It is as easy to create an array of arrays as it is to create a single dimensional array. Here are the steps to create an array of arrays:

  1. Create an array and store it in a variable.
  2. Create additional arrays, and store them in variables as well.
  3. Use the previously created arrays and separate them with the comma operator.
  4. Assign the arrays to a variable.

The following code creates four arrays and stores the arrays in variables. It then assigns each of the newly created arrays to a different element in a new array named $array.

PS C:\> $a = 1..5

PS C:\> $b = 6..10

PS C:\> $c = 11..15

PS C:\> $d = 16..20

PS C:\> $array = $a,$b,$c,$d

Accessing elements in an array of arrays

To access items from an array of arrays, use the square-bracketed “array” notation. For example, to see the first array of items, which are stored in element 0, use [0]. This code is shown here.

$array[0]

What is cool is that element 0 contains an array, so an array returns in response to the command. In the following command, I return the array previously stored in element 0. To save space in the output, I join the elements of the array with a space between each element. Here is the command to display the array that is stored in element 0 and to return the elements on a single line.

PS C:\> $array[0] -join " "

1 2 3 4 5

Each of the other arrays that I stored in the other elements of $array are accessed in the same manner. The following code displays the information that is contained in the remaining elements of the $array array.

PS C:\> $array[1] -join " "

6 7 8 9 10

PS C:\> $array[2] -join " "

11 12 13 14 15

PS C:\> $array[3] -join " "

16 17 18 19 20

If I want to access a specific element from one of the arrays that is stored in one of the elements of the $array array, I again use the square bracket (array) notation. This time, I also specify the specific element from the array that is contained in the element. Therefore, to access element 1 in the $b array (stored in element 1 of $array), I use the notation that is shown here.

$array[1][1]

To access the third element of the array, which is stored in the third element of $array, the syntax is as shown here.

$array[3][3]

The following image shows the code and the output from creating four arrays and storing them in variables a-d, creating an array that holds the four arrays, displaying the values from each element, and then accessing element 1,1 and element 3,3.

Image of command output

One of the fun things to do, instead of just storing numbers in arrays, is to use the array for temporary storage. For example, in the following code, I store the results of three different WMI queries into three different variables. I then create three different arrays with cherry-picked properties from each of the stored WMI results. Next, I create an array of arrays by building a new array.

$comp = gwmi win32_computersystem

$os = gwmi win32_operatingsystem

$bios = gwmi win32_bios

$a = $comp.UserName, $comp.DNSHostName, $comp.Domain

$b = $os.caption, $os.buildnumber, $os.OSArchitecture

$c = $bios.name, $bios.description, $bios.manufacturer

$array = $a,$b,$c

When I have my array of arrays, I can index into any of the values that I need to access. A few of the elements are shown here.

PS C:\> $array[0]

IAMMRED\ed

newMrEd

iammred.net

PS C:\> $array[0][1]

newMrEd

PS C:\> $array[1]

Microsoft Windows 7 Ultimate

7601

64-bit

PS C:\> $array[1][0]

Microsoft Windows 7 Ultimate

PS C:\> $array[2][1]

BIOS Date: 05/04/11 17:11:33 Ver: 04.06.04

The commands to store three different WMI queries in three different variables, build up an array of arrays, and access different elements of the arrays are shown in the following image.

Image of command output

One thing that has intriguing possibilities is to store rich objects in an array. In the following example, I store the results of Get-Service, Get-Process, and Get-Date in their own variables. Next, I create an array of these objects.

$a = Get-Service

$b = Get-Process

$c = get-date

$array = $a,$b,$c

When I have the array of objects stored, I can index into them just like anything else.

PS C:\> $array[2]

Thursday, December 01, 2011 5:31:12 PM

Because there is only one DateTime object, if I attempt to go any deeper with this object, an error arises. The command and the associated error are shown here.

PS C:\> $array[2][1]

Unable to index into an object of type System.DateTime.

At line:1 char:11

+ $array[2][ <<<< 1]

    + CategoryInfo          : InvalidOperation: (1:Int32) [], RuntimeException

    + FullyQualifiedErrorId : CannotIndex

But with the processes and services in the array elements, we have a collection of arrays. I can access them directly. I illustrate this here.

PS C:\> $array[1][0]

 

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName

-------  ------    -----      ----- -----   ------     -- -----------

    671      41    31700      38604   187   100.79   7284 AnalogRec13

 

In fact, I can even use dotted notation to access individual property values from individual processes. This technique is shown here.

PS C:\> $array[1][0].id

7284

Well, that is all there is to using and working with arrays. Tomorrow, I will talk about hash tables. Hope to see you then.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Create a Hash Table in PowerShell that Contains Hash Tables

$
0
0

Summary: Learn how to work with hash tables that contain other hash tables in Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. Well, it is a typical winter day here in Charlotte, North Carolina in the United States. I am not talking about a nice, cool, sunny day with cobalt blue skies streaked by fluffy cotton candy clouds—nope, that is the “chamber of commerce” picture. A typical winter day around here seems to be cool, damp, and drizzled with rain—at least that is the way it has been for the past several days. That is OK; I figure we need all the rain we can get, as long as it does not come all at once.

I thought I would get up early and work on my presentation for the Pittsburgh PowerShell User Group meeting. The Scripting Wife and I will be there for the first meeting, and we are both really looking forward to it. Space is limited at the meeting; therefore, it is essential to sign up early to obtain a ticket (the tickets are free).

This past week, I talked about working with arrays. Although Windows PowerShell makes working arrays very easy, there are still lots of little things that need special attention. The five articles in the series devoted to working with Arrays in Windows PowerShell are listed here:

One of the cool things to do with Windows PowerShell is to create a hash table that contains other hash tables. Creating a hash table that contains other hash tables offers a decent amount of flexibility, but it can become a bit confusing. Today, I thought I would spend a little time to help alleviate some of that confusion.

To create a hash table, I use the “hash table” operator that consists of an at sign (@), and a pair of curly brackets. Remember, that a hash table consists of one or more key/value pairings. For example, I create a hash table that consists of three items. This hash table is stored in the hashtable variable as shown here.

$hashtable = @{1="one";2="two";3="three"}

When I examine the hash table, I see that the numbers are the name (key) elements, and the words are the associated value. This is shown here.

PS C:\> $hashtable

 

Name                           Value

----                           -----

3                              three

2                              two

1                              one

If I create an additional hash table, and I attempt to add it to the hash table that is stored in the hashtable variable, an error occurs. The error is shown in the following image.

 Image of command output

Receiving an error is certainly not my desired result. I then decide to use the += operator to add the two hash tables together. This technique is shown in the following image.

Image of command output

Unfortunately, merely adding the two hash tables together, does not allow me to have a hash table of hash tables. It creates a larger hash table and automatically incorporates the elements from the new hash table into the old hash table.

To create a hash table that contains other hash tables requires remembering that a hash table is comprised of key/value combinations. Therefore, I use the Add method, but I first add a key value, then I store the hash table in the value portion. The syntax is shown here.

$newhash.Add("hashtable",$hashtable)

In the following image, I create two hash tables. I then create an empty hash table and call the Add method to add the two hash tables to the newly created one.

 Image of command output

When I have a hash table that contains other hash tables, I can use the Values property to view all the properties that are contained in the hash table. This command is shown here.

PS C:\> $newhash.Values

 

Name                           Value

----                           -----

5                              five

4                              four

3                              three

2                              two

1                              one

In addition to viewing the values, I can view the keys in the hash table by using the Keys property. This command is shown here.

PS C:\> $newhash.Keys

h

hashtable

I can also use the Item method to retrieve a specific hash table. To do this, I use the Key property, which displays in the output as a Name column as shown here.

PS C:\> $newhash.Item("h")

 

Name                           Value

----                           -----

5                              five

4                              four

 

PS C:\> $newhash.Item("hashtable")

 

Name                           Value

----                           -----

3                              three

2                              two

1                              one

In addition to retrieving the hash tables that are stored in the newhash hash table via the Item method, I can also retrieve specific items from the stored hash tables. This technique is shown here.

PS C:\> $newhash.Item("hashtable").item(1)

one

PS C:\> $newhash.Item("h").item(4)

four

It is possible to work with the hash tables that are stored in the hash table in the same way that one works with other hash tables. For example, suppose I want to create two hash tables, and I store those two hash tables in a new hash table. The code to do this is shown here.

$a = @{"dog"="dog food";"cat"="cat food"}

$b = @{"people"="people food"}

$c = @{"one"=$a;"Two"=$b}

I can use the Keys property to retrieve the keys from one of the hash tables, and I can use the Values property to look at the values. This technique is shown here.

PS C:\> $c.Item("two").keys

people

PS C:\> $c.Item("two").values

people food

In addition, I can use the Add method to add key/value pairs to one of the hash tables. This appears here.

PS C:\> $c.Item("two").add("more people","more people food")

PS C:\> $c

 

Name                           Value

----                           -----

Two                            {people, more people}

one                            {dog, cat}

 

Well, that is about all there is to working with hash tables that contain other hash tables.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Combine Arrays and Hash Tables in PowerShell for Fun and Profit

$
0
0

Summary: Take your Windows PowerShell scripting to the next level by combining hash tables and arrays.

Microsoft Scripting Guy, Ed Wilson, is here. The Scripting Wife and I are anxiously counting down the days until the first ever Pittsburgh PowerShell Users Group meeting. The event is quickly selling out; therefore, if you want to attend, you will need to sign up for the meeting. The reason for the reservation is that space at the Pittsburg Microsoft Office is limited. The meeting will be a lot of fun.

This past week, I talked about working with arrays. Although Windows PowerShell makes working arrays very easy, there are still lots of little things that need special attention. The five articles in the series devoted to working with Arrays in Windows PowerShell are listed here:

Today, I want to look at creating an array that contains hash tables. Suppose I create a couple of hash tables, such as the ones that are shown here.

$a = @{"one"=1;"two"=2}

$b = @{"three"=3;"four"=4}

If I want to create an array with these two hash tables, it is as simple as using the equality operator, and assigning them to a variable by using standard array techniques. This command is shown here.

$c = $a,$b

The view of this array of hash tables appears to be a bit strange—it looks like I am looking at a hash table. This is shown here.

PS C:\> $c

 

Name                           Value

----                           -----

two                            2

one                            1

three                          3

four                           4

The cool thing is that I can index into the elements of my array by using square brackets as shown here.

PS C:\> $c[0]

 

Name                           Value

----                           -----

two                            2

one                            1

Because each element of the array contains a hash table, I can index into the array, and then use “hash table kinds of commands” such as querying the Keys or Values properties or using the Item method. These commands are shown here.

PS C:\> $c[0].values

2

1

PS C:\> $c[0].keys

two

one

PS C:\> $c[0].item("one")

1

The commands that create two hash tables, add the hash tables to an array, and then index into the array to work with the hash tables are shown in the following image.

Image of command output

If I am only interested in the keys that the hash tables contain, I can pipe the array to the Select-Object cmdlet. This technique is shown here.

PS C:\> $c | select keys

 

Keys

----

{two, one}

{three, four}

Although the output does contain the keys, it is not the best output. I can clean things up a bit by using the ExpandProperty parameter. This command is shown here.

PS C:\> $c | select -ExpandProperty keys

two

one

three

four

That is about all there is to working with an array of hash tables. Join me tomorrow as I begin a new week with a special guest blog post by Karl Mitschke.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Use a PowerShell Script to Start Non-Running Automatic Services

$
0
0

Summary: Guest blogger, Karl Mitschke, discusses a Windows PowerShell script to start non-running services that are set to start automatically.

Microsoft Scripting Guy, Ed Wilson, is here. Our guest blogger today is Karl Mitschke, one of the authors of the Windows PowerShell 2.0 Bible. Here is a little bit about Karl.

Karl Mitschke is an IT veteran with over 20 years of experience. He has specialized in messaging since the early 1990s. Karl has been automating tasks with script since moving to Microsoft Exchange 5.0, starting with WinBatch. He started using Windows PowerShell in 2007 when he moved to Exchange Server 2007. When he’s not writing Windows PowerShell scripts, or writing about  Windows PowerShell scripts, Karl enjoys spending time with his bride, Sherry, and their two dogs.
Blog: Unlock-PowerShell
Windows PowerShell 2.0 Bible

Starting services on remote servers

In the “Performing Advanced Server Management” chapter in the Windows PowerShell 2.0 Bible, I presented a script that would start services on remote servers that are set to start automatically and were not started. Due to the constraints of the book, the script was severely limited. That script is shown here.

$Computers = “FileServer01”,”FileServer02”
$WmiObject = @{
Class = “Win32_Service”
Filter = “StartMode=’Auto’ and State!=’Running’”
}
foreach ($Computer in $Computers)
{
foreach ($Svc in Get-WmiObject @WmiObject -ComputerName $Computer)
{
Write-Host “Starting the” $Svc.DisplayName “service on $Computer”
$Svc.StartService() | Out-Null
}
}

This was fine, but the script could really use some improvement. For one thing, you needed to hard code the server names. In addition, it did not accept credentials, you could not simulate the action, and there was no Help.

The concept was sound, however, so I decided to extend the script to put it into production at work, where I use it to verify that services are running after performing updates on our Exchange Server infrastructure. Unfortunately, extending the script makes it too long for the book and too long for this blog. The complete script is available in the Scripting Guys Script Repository, but I will discuss highlights of the script in this blog post.

Extending the script with Help

The first several objections are easy to overcome with advanced parameters. The final objection is overcome with comment-based Help. Windows PowerShell even includes a method to add Help. I am referring to the built in Get-Help cmdlet. In fact, you can potentially get more Help than you will ever need by typing the following script into your Windows PowerShell console:

Get-Help -Name functions_advanced_parameters

Get-Help -Name about_comment_based_help

The Help about advanced parameters is too verbose to summarize, so I would suggest that you read it and familiarize yourself with the content.

The Help for comment-based Help shows that for script-based Help, comment-based Help can appear in two locations:

  • At the beginning of the script file. Script Help can be preceded in the script only by comments and blank lines. If the first item in the script body (after the Help) is a function declaration, there must be at least two blank lines between the end of the script Help and the function declaration. Otherwise, the Help is interpreted as being Help for the function, not Help for the script.
  • At the end of the script file. If the script is signed, place comment-based Help at the beginning of the script file. The end of the script file is occupied by the signature block.

As I sign the scripts that I use at work, I include comment-based Help at the beginning of the file.

I also choose to use the comment-block syntax as opposed to using the comment character (#) in front of each line, because I find it to be more readable. An example of the comment-block syntax is shown here.

<#

.SYNOPSIS

Invoke-StartService - Start all essential services that are not running.

.DESCRIPTION

This script finds services that are set to start automatically, and starts them.

#>

Therefore, Help is out of the way with the comment-based Help. Next, I tackle the parameters.

Adding arguments

I wanted to allow the script to accept server names from the command line or from a pipeline, so I used the ValueFromPipeline argument when I declare the ComputerName parameter for the script. I also want to allow the script to target multiple computers, so I declared the ComputerName parameter as an array of strings as shown here.

[Parameter(

Position = 0,

ValueFromPipeline=$true,

Mandatory = $false,

HelpMessage = "The computer to start services on"

)]

[string[]]$ComputerName = $env:ComputerName

As you can see, I set the ComputerName parameter to not be required by using the local computer name if it is not specified. I also set the parameter to be positional—that means that you do not need to use the name of the parameter as long as the computer name(s) are specified as the first-passed parameter to the script.

I wanted the script to accept credentials so that I could start my Windows PowerShell session with a non-administrator account. I added the optional parameter Credential to handle these credentials.

Finally, I wanted to emulate the WhatIf functionality in so many of my favorite cmdlets, so I added the optional switch parameter WhatIf. The entire parameter declaration is shown here.

param (

[Parameter(

Position = 0,

ValueFromPipeline=$true,

Mandatory = $false

)]

[string[]]$ComputerName = $env:ComputerName,

[Parameter(

Position = 1,

Mandatory = $false

)]

$Credential,

[Parameter(

Mandatory = $false

)]

[switch]$WhatIf

)

Using $PSBoundParameters

I wanted to avoid If statements as much as possible, so I take advantage of the automatic variable $PSBoundParameters, which is a hash table of the parameter names and values that are passed to the script.

You can pass the $PSBoundParameters to the Get-WmiObject cmdlet as a splatted hash table as shown here:

Get-WmiObject -Class Win32_Service @PSBoundParameters

The parameters ComputerName and Credential can be passed directly to the Get-WmiObject cmdlet. However, that cmdlet does not accept the WhatIf parameter, if present, which would cause the error message shown here.

Image of error message

This can be resolved by using the Remove() method of the hash table. You can check for the existence of the parameters with an If statement, or you can pass the command to the Out-Null cmdlet to avoid the If statement as shown here.

$PSBoundParameters.Remove("WhatIf") | Out-Null

I also have to handle the two methods of passing credentials to the script. The parameter Credential is not typed, so it accepts a string or a credential object as shown here.

$cred = Get-Credential -Credential "mitschke\karlm"

.\Invoke-StartService.ps1 -Credential $cred

When it is supplied like that, the $PSBoundParameters hash table passes a credential object to the Get-WmiObject cmdlet. However, if you supply the Credential parameter with a string, allowing the script to prompt for a password, the $PSBoundParameters hash table passes the string to the Get-WmiObject cmdlet and prompts for a password on every service that needs starting.

This is resolved by once again using the Remove() method of the hash table, and then using the Add() method to add the valid credential object to the $PSBoundParameters hash table as shown here.

if ($Credential -ne $null -and $Credential.GetType().Name -eq "String")

{

$PSBoundParameters.Remove("Credential") | Out-Null

$PSBoundParameters.Add("Credential", (Get-Credential -Credential $Credential))

}

I modified the Filter parameter for the Get-WmiObject cmdlet to find only the services that I was interested in. Specifically, I am not interested in starting the Microsoft .NET Framework Runtime Optimization Services, which may be set to start automatically. The service would start and then stop because it finds that no action is needed. It is not really a problem starting these services, I just wanted to avoid the time it takes to start them, and avoid event log entries that show they had started and stopped.

These services have a service name starting with clr_optimization_. The filter I use is shown here.

Filter "startmode='auto' and state='stopped' and (name > 'clra' or name < 'clr')"

The filter isn’t perfect—I suppose there could be a service with a name that starts with clra, but I have not seen one, so the filter serves my purposes. A better filter could use the grave accent character (`). The grave accent character is also the escape character for Windows PowerShell, so you would need to use two grave accents as “name > 'clr``’”. You could also use a client-side filter by using the Where-Object cmdlet as shown here:

Get-WmiObject -Class Win32_Service | Where-Object -FilterScript {$_.Name -notmatch "clr_*" -and $_.StartMode -eq "Auto" -And $_.State -ne "Running"}

That is perfectly valid; however, using the Filter parameter of the Get-WmiObject cmdlet performs server-side filtering, which should be quicker because only the objects we are interested in are passed back to the client. The complete server-side filtering example is shown here:

Get-WmiObject -Class Win32_Service –Filter "startmode='auto' and state='stopped' and (name > 'clra' or name < 'clr')"

So now we have a working filter, working parameters, and working Help. It is now time to test the parameters with the WhatIf switch. The command line and its associated output are shown here.

Image of command output

When I see what the script will do, I run the script without the WhatIf parameter. The output from the command appears in the following image.

Image of command output

As mentioned, the entire script is available in the Scripting Guys Script Repository. The output of the script follows the script. I believe the script is pretty well written; but as always, I am open to suggestions. You can reach me at:

-join ("6B61726C6D69747363686B65406D742E6E6574"-split"(?<=\G.{2})",19|%{[char][int]"0x$_"})

Thank you Karl, for a very useful and interesting blog post. Join me tomorrow for more Windows PowerShell goodness.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell to Search Active Directory for GIDs

$
0
0

Summary: Learn how to use Windows PowerShell to search Active Directory for GIDs.

So what is a GID? To the UNIX (and Linux) operating systems, group identifiers (GIDs) and user identifiers (UIDs) are what security identifiers (SIDs) are to the Windows operating system. Each user in the UNIX system has a UID and a primary GID to identify the primary group he belongs to. Users can also belong to multiple secondary groups to inherit security settings.

In a pure Windows world, these fields are not used, even though the uidNumber and gidNumber attributes are available for compliance to LDAP protocol. The following image shows that the gidNumber attribute is not set.

 

In a mixed Windows and UNIX environment, however, the uidNumber and gidNumber attributes provide a mechanism to map UNIX users and groups to Windows names. For example, after Services for NFS are installed, Active Directory lookup services utilize these attributes for identity management.

So how can we query groups that have GIDs assigned? As always, there are multiple ways to accomplish this in the Windows operating system. Windows PowerShell has made searching through LDAP much easier by implementing the [adsisearcher] type accelerator, which instantiates a System.DirectoryServices.DirectorySearcher .NET object from a search filter.

As a start, we can use "(objectCategory=group)" as the filter string to enumerate Active Directory groups as shown here.

$searcher=[adsisearcher]"(objectCategory=group)"

$result = $searcher.FindAll()

$result | Select-Object

This script and its associated output are shown here.

Image of command output

This simple search filter uses the "(<attribute><operator><value>)" format that is mentioned in the Services for NFS Step-by-Step Guide. Now to find all groups that have the gidNumber attribute set, we need another filter that checks this attribute, and "(gidNumber=*)" will do the trick. By combining the two filters with the "(<operator><filter1><filter2>)" format, we get the following:

"(&(objectCategory=group)(gidNumber=*))"

This script and its associated output follow. 

$searcher=[adsisearcher]"(&(objectCategory=group)(gidNumber=*))"

$result = $searcher.FindAll()

$result | Select-Object @{Name="DN";Expression={$_.properties.distinguishedname}},@{Name="gid";Expression={$_.properties.gidnumber }}

Image of command output 

The previous script would have worked just fine if objectClass was used in place of objectCategory. An interesting comparison between the two is documented in this Windows Dev Center topic.

One can easily append an Export-Csv cmdlet at the end of the previous command to save the result as a .csv file for reading or processing in the future.

With the Active Directory module that’s available for Windows 7 or Windows 2008 R2, there are multiple ways to achieve the same goal by using a single cmdlet. Two options are shown here. 

#Use LDAP search filter syntax

Get-ADObject -LDAPFilter "(&(objectCategory=group)(gidNumber=*))" -Properties gidNumber | Select @{Name="DN";Expression={$_.DistinguishedName}},@{Name="gid";Expression={$_.gidNumber}}

#Or, use AD filter syntax

Get-ADGroup -filter 'gidNumber -like "*"' -Properties gidNumber | Select @{Name="DN";Expression={$_.DistinguishedName}},@{Name="gid";Expression={$_.gidNumber}}

This command output is shown here.

Image of command output

Note: The Properties parameter is needed in both commands to include the gidNumber property (which is not exported by default) in the resultant objects.

We can actually query Active Directory objects through WMI. The DS_GROUP class under the "root\directory\ldap" namespace instantiates all group objects from Active Directory. So with a WQL filter, we can retrieve a list of groups with the gidNumber attribute defined as shown here.

Get-WmiObject -namespace root\directory\ldap -class ds_group -filter 'DS_gidNumber IS NOT NULL' | Select-Object @{Name="DN";Expression={$_.DS_distinguishedName}},@{Name="gid";Expression={$_.DS_gidnumber }}

Image of command output

Notice how WMI uses the "DS_" prefix for both the distinguishedName and gidNumber attributes.

Now with all these options available, which route has the best performance? This question will be left to the readers to test in a real environment. As a hint, Measure-Command is the cmdlet to assess the execution time of script blocks.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy


Use PowerShell to Find and Uninstall Software

$
0
0

Summary: Learn how to use Windows PowerShell to get software installation locations, and to uninstall software from remote computers.

Hey, Scripting Guy! QuestionHey, Scripting Guy! We have a dumb application that we have to use at work. The company has released a new version of this application, and I am trying to write a Windows PowerShell script to uninstall the old application—the problem is that I need to find the application first. I tried looking in the registry, but the install key is empty…figures. Like I said, this is a really dumb application. I read the guest blog written by Marc Carter about problems using the Win32_Product WMI class, but it looks like I am going to be stuck using this anyway. The problem is that it is really slow. Is there any way to speed this thing up? I have to query over a thousand computers, and in our testing, this query takes nearly five minutes to complete—that would be three and a half days for only one query.

—BT

Hey, Scripting Guy! Answer Hello BT,

Microsoft Scripting Guy, Ed Wilson, is here. The Scripting Wife and I were in Texas for the Corpus Christi Windows PowerShell User Group meeting when Marc Carter told me about the problem with the MSI installer reconfiguring applications when the Win32_Product WMI class is queried. I immediately encouraged him to write a guest blog about this issue.

BT, there is a way to use the Win32_Product WMI class in a more efficient manner. It relies on using the [WMI] type accelerator, instead of doing a generic WMI query. The problem is that the [WMI] type accelerator returns a specific instance of a WMI class. To connect to a specific instance, I must use the Key property of a WMI class.

I can use the Get-WMIKey function from my HSGWMImoduleV6 module. In the following code, I first import my HSGWMImoduleV6 module, and then I use the Get-WMIKey function to return the key to the Win32_Product WMI class. The commands and the output from the commands are shown here.

PS C:\> Import-Module hsg*6

PS C:\> Get-WmiKey win32_product

IdentifyingNumber

Name

Version

The Key property for Win32_Product is a composite key comprised of IdentifyingNumber, Name, and Version. The easy way to get this information is to use the Get-WmiObject cmdlet to query for the information. I only need to do this once, and I will have the three pieces of information. A table is a nice way to display the information. In the code shown here, I use the Get-WmiObject cmdlet (gwmi is an alias) to return product information, and then I pipe the management objects to the Format-Table (ft is an alias) cmdlet for display.

gwmi win32_product | ft name, version, ident*

In the image that follows, I import the HSGWMIModuleV6 module, use the Get-WMIKey function to retrieve the Key property of the Win32_Product WMI class. I then use the Get-WmiObject cmdlet (gwmi is an alias) to query the Win32_Product WMI class, and I output the management objects to a table via the Format-Table (ft is alias) cmdlet. The following image displays the commands and the output from the commands.

Image of command output

BT, you may ask, “What about Marc Carter’s warning about using the Win32_Product WMI class? “ Well as seen in the results from querying the event log, it is a concern. Shortly after querying the Win32_Product WMI class, I used the Get-EventLog cmdlet to query the application log for MSIInstaller events. The following image shows massive product reconfiguring going on.

Image of command output

The query to return the three parts of the composite key only needs to run once; the values do not change. It is also possible to use the Get-WmiObject cmdlet and a filter to improve the performance of the command a little bit. The nice thing about this command is that it returns the information that is required by the [WMI] type accelerator. The command and associated output are shown here.

PS C:\> gwmi win32_product -filter "Name LIKE '%Silverlight%'"

IdentifyingNumber : {89F4137D-6C26-4A84-BDB8-2E5A4BB71E00}

Name              : Microsoft Silverlight

Vendor            : Microsoft Corporation

Version           : 4.0.60831.0

Caption           : Microsoft Silverlight

When I have the three pieces of information (the IdentifyingNumber, the Name, and the Version), it is time to create the key. This is where quite a bit of experimentation could be required. I have to use the back tick (grave) character to escape inside quotation marks. I have to escape the quotation mark and the opening curly bracket for the IdentifyingNumber property. I also have to escape the closing curly bracket and the closing quotation mark. I then have to escape the quotation marks that surround Microsoft Silverlight, in addition to the quotation marks for the Version property. There are also two quotation marks at the end of the ClassKey.

Here is the key I derived for Microsoft Silverlight on my computer. (This is a single line command. A space between Microsoft and Silverlight exists, but other than that, there are no spaces).

$classKey="IdentifyingNumber=`"`{89F4137D-6C26-4A84-BDB8-2E5A4BB71E00`}`",Name=`"Microsoft Silverlight`",version=`"4.0.60831.0`""

The reason for all the escaping in the ClassKey, is that WMI expects the quotation marks and the curly brackets in the key itself. To see what WMI expects to receive via the command, I use the Windows Management Instrumentation Tester (WbemTest) command, and I view the instances of the class. The following image illustrates the instances of Win32_Product on my computer.

Image of query results

When I have the ClassKey, I can use the [WMI] type accelerator to connect to the specific software package (Microsoft Silverlight in this example). In fact, using the [WMI] type accelerator is very easy. Here is the command. (The command is [WMI], the class name, and the key).

[wmi]"Win32_Product.$classkey"

I can also include the WMI namespace (really important if the class resides in a namespace other than the default root\cimv2). In the command that follows, notice that there is a backslash that precedes the word root. One other thing to notice is that a colon separates the WMI namespace and the WMI class name.

[wmi]"\root\cimv2:Win32_Product.$classkey"

If I need to connect to a WMI class on a remote computer, I use a double backslash and the name of the computer, then the WMI namespace, the WMI class, and the WMI ClassKey. The command that follows illustrates this.

[wmi]\\remotehost\root\cimv2:Win32_Product.$classkey

In the image that follows, I illustrate the different ways of querying WMI for Microsoft Silverlight software. I then compare the speed of using the Get-WmiObject cmdlet against the speed of using the [WMI] type accelerator. As shown in the following image, the Get-WmiObject cmdlet, using the filter to find Microsoft Silverlight, takes over five seconds on my laptop. Using the [WMI] type accelerator takes less than one-half of a second. This is more than 10 times faster.  

Image of results

By the way, there was not much difference between using the filter to look for Microsoft Silverlight or using the Where-Object. In the following output, I use the Measure-Object cmdlet to determine the performance of using the Where-Object (the ? is an alias for Where-Object).

PS C:\> measure-command {gwmi win32_product | ? {$_.name -match 'silverlight'}}

Days              : 0

Hours             : 0

Minutes           : 0

Seconds           : 5

Milliseconds      : 311

Ticks             : 53112518

TotalDays         : 6.14728217592593E-05

TotalHours        : 0.00147534772222222

TotalMinutes      : 0.0885208633333333

TotalSeconds      : 5.3112518

TotalMilliseconds : 5311.2518

If you suspect that the problem with the filter is that I used the like operator as opposed to the equality operator, that is not the case. Here are the results from using the equality operator.

PS C:\> measure-command {gwmi win32_product | ? {$_.name -match 'silverlight'}}

Days              : 0

Hours             : 0

Minutes           : 0

Seconds           : 5

Milliseconds      : 311

Ticks             : 53112518

TotalDays         : 6.14728217592593E-05

TotalHours        : 0.00147534772222222

TotalMinutes      : 0.0885208633333333

TotalSeconds      : 5.3112518

TotalMilliseconds : 5311.2518

When using the [WMI] type accelerator, a complete instance of the WMI class instance returns. The properties and their associated values are shown in the following image. Notice two properties: the __Path (that is, double underscore Path) property is the key to the WMI class instance. The InstallLocation property points to the location where the software installs.

Image of results

BT, you did not ask, but there is an Uninstall method available from the Win32_Product WMI class. It appears only on instances of the class. Therefore, it is possible to uninstall software by using the command that is shown here. (If I want to uninstall from a large collection of servers, I use the foreach statement ($servers is an array of server names).

foreach($server in $servers)

{ ([wmi]"\\$server\root\cimv2:Win32_Product.$classKey").uninstall() }

BT, that is all there is to using the Win32_Product WMI class to detect or to uninstall software. Join me tomorrow when I will have a guest blog written by Raymond Mitchel as he talks about Windows PowerShell and SharePoint.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Use PowerShell to Inventory and Update SharePoint Environments

$
0
0

Summary: Learn how to use Windows PowerShell to inventory objects in your SharePoint environment and to script updates.

Microsoft Scripting Guy, Ed Wilson, is here. Raymond Mitchell is our guest blogger today. Here is a little bit about Raymond.

Photo of Raymond Mitchell

Raymond has worked in IT since 1999, and he has worked with every version of SharePoint since its release as SharePoint Team Services. He has spent the last eight years consulting on all things SharePoint. He helped start the Minnesota SharePoint User Group, and he coordinated it for seven years. He has spoken at a number of other user groups and SharePoint Saturday events. Raymond wrote a Wrox Blox about the Data View Web Part and contributed two chapters on the BCS to SharePoint 2010 Six-in-One. He also blogs under the alias IWKID about information worker technologies, including SharePoint and Office. He currently works as a SharePoint consultant at SharePoint911.

As a SharePoint administrator, it is only a matter of time until someone asks, “How many sites do we have? How many lists? How many of these web parts?” There are lots of variations of these questions, but essentially someone is asking you to inventory objects in your SharePoint environment. If you’ve had any experience with database-backed applications, you might be tempted to run to the SharePoint content databases and start running queries, but those databases are strictly off-limits in SharePoint. Luckily we have Windows PowerShell to help us out!

Before we jump into scenarios, let’s make a few assumptions:

  • We’re running Windows PowerShell scripts on the SharePoint Server
  • We’re running SharePoint 2010 and leveraging the SharePoint Management Shell

It is not that our scenario requires these assumptions, but for brevity, we are keeping things simple.

Finding SharePoint sites

To get started, let us take a look at the first question: “How many sites do we have?” Unfortunately this question is not as straightforward as it sounds. The following table shows that in SharePoint, there are three separate objects that could easily be confused with “sites.”

Object

Relationship

Object Model Class

Web Application

Can span multiple IIS sites

SPWebApplication

Site Collection

Web Applications can contain multiple site collections

SPSite

Web

Each site collection typically contains multiple webs (a RootWeb and its sub webs)

SPWeb

In this example, we will assume that they are asking about how many webs, so we will first get the Web Application object, and then count the webs for each site collection. The following image illustrates how to accomplish this task. (This code has been uploaded to the Script Center Repository.)

Image of script

Because we are using SharePoint 2010 and the 2010 Management Shell (which is preloaded with the Microsoft.SharePoint.PowerShell snap-in), we can leverage cmdlets like Get-SPWebApplication which makes life considerably easier.

Inventorying SharePoint lists

Next, let us take a look at how many lists we have. Again this question might not be as straightforward as it sounds. In SharePoint, there are two main content containers that could be included in this inventory: Lists and Libraries.

Object

Description

Object Model Class

List

Contains list data

SPList

Library

Contains documents

SPDocumentLibrary

By checking a list’s BaseType property, we can filter out any Document Libraries and return only the count of lists (we could also filter on the BaseTemplate to filter on specific List types such as Announcements or Tasks). Because we are dealing with more objects, in this example we are going to work with a single site collection to reduce our performance impact on the server. The following image illustrates this technique.

Image of script

Of course, it would be better to see where the lists exist. To do this requires a bit more work as shown in the following image. (This code can be found in the Script Center Repository.)

Image of script

Finding specific web parts

Great! Now we know where our lists are! In our final scenario, we will take a look at finding specific web parts in a site. In this example, we will keep things simple and assume that the web parts are stored on the homepage of a site (known as the WelcomePage). We will limit our inventory to a single site collection, and we will look for Content Editor Web Parts. This technique is shown in the following image.

Image of script

Again, just knowing how many items is not usually the end of the story. In this case, let us suppose that we want to remove all of the Content Editor Web Parts from the site. We will need to update the script to first inventory the web parts (the important things are their web pages and IDs), and then we’ll go back and delete them one at a time (because we can’t update the collection as we’re looping through).

Another interesting hiccup occurs when we try to open the website that contains the page. The Get-SPWeb and Get-SPSite cmdlets are not as forgiving as the constructors for these classes, so if we pass in a URL for a page within the site, it will not return the object for us. Instead, I’m using New-Object to create an SPSite object, rather than the cmdlet (by the way, this is how you had to do it when you were working with SharePoint prior to the Management Shell). Here is the code for removing the web parts. (This code can be found in the Script Center Repository also.)

Image of script

In summary, we have explored how to inventory webs, lists, and web parts in our SharePoint site. We have also shown how you can work with classes from the SharePoint Object Model to extend your inventory scripts to make updates across your environment.

The value of Windows PowerShell to SharePoint administrators extends far beyond the cmdlets that ship with SharePoint 2010 and options like Backup and Restore. Windows PowerShell gives administrators the ability to rapidly inventory and update their environments.

~Raymond

Thank you, Raymond, for a great Windows PowerShell and SharePoint blog post. Join us tomorrow for more cool Windows PowerShell stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Use PowerShell to Simplify Access to Data Through PowerCLI

$
0
0

Summary: Two new features in VMware's PowerCL make it easy for Windows PowerShell to gather virtualization configuration information.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have a guest blog written by Josh Atwell. Josh is an IT pro who spends a lot of his time using Windows PowerShell and PowerCLI. Today he is going to talk about some of his favorite tasks to perform by using these tools. Take it away Josh…

When I was approached by the Scripting Guys to guest blog this holiday season, I JUMPED at the opportunity. My exclamations scared the cat and led to a funny look from the Mrs. Why so excited? Because I was being given an opportunity to share with more people my favorite use of Windows PowerShell: VMware's PowerCLI snap-in.

Using the VMware PowerCLI snap-in

The growing use of datacenter virtualization in organizations means more servers, more configurations, and more reporting for administrators to manage. Fortunately for all of us, Microsoft and VMware have worked hard to increase Windows PowerShell functionality in these environments. I would say they've done an amazing job; but unfortunately, gathering information in large environments can be very time consuming, even with Windows PowerShell. I'd like to share two of the best additions to VMware's PowerCLI and how to use them to get information from the SDK quickly and efficiently. The two additions are ExtensionData and New-VIProperty

Using the Get-View cmdlet

Gathering information with PowerCLI is generally straightforward, but there are times where the cmdlets don't provide all of the information about an object that you may need. VMware addressed this problem with the Get-View cmdlet. The Get-View cmdlet allows you to retrieve .NET view objects based on the criteria that you provide for the object type and based on filters. With some practice, you can use Get-View to retrieve data from the VMware SDK very quickly. These calls are essentially raw data grabs, and they lack the Windows PowerShell processing provided by traditional cmdlets like Get-VM.

Let's give it a shot and search for information about my virtual machine "w2k8-std-32-2". We already know the Virtual Hardware version of the virtual machine can be grabbed quickly through vCenter Server; but for demonstration purposes, let's assume that it has to be grabbed as part of a Windows PowerShell script. I'm hoping it's on Virtual Hardware version 7 because I need to take advantage of some specific features for that hardware version. Note that in the code shown here, the Filter parameter requires a hash table for the input. 

Get-View -ViewType VirtualMachine -Filter @{"Name" = "w2k8-std-32-2"}

This initial call returns and is significantly different than what is provided by a simple Get-VM call. This is shown in the image that follows.

 Image of command output

At first glance, the information here is pretty cryptic, but notice that you now have visibility into a variety of different properties such as the virtual machine’s Config information. We'll set our Get-View call to a variable and dig into Config to see what we can find there. I use the code shown here to examine the Config property.

$vmview = Get-View -ViewType VirtualMachine -Filter @{"Name" = "w2k8-std-32-2"}

$vmview.Config

The command and output associated with that command are shown in the following image.

Image of command output

I have condensed the information provided because it is quite extensive, but you can see right away that Version = vmx-07! Fantastic! Although it's not as simply put as in vCenter server, it wasn't too difficult. Using Get-View made it a very quick call and return.

Get-View calls can start to get quite complex and difficult if you are not familiar with the VMware SDK. VMware addressed this in a big way with their 4.1 release of PowerCLI. 

Introducing ExtensionData!

Taking a look into the virtual machine object, you see a new property called ExtensionData. ExtensionData was released with PowerCLI 4.1, and it provided Windows PowerShell folks a new and easier mechanism for getting into the vCenter SDK. Did I mention that it's easy to use? It's SUPER easy to use! In the image that follows, I use the Get-View cmdlet and pipe the output to the Get-Member cmdlet to display the ExtensionData property.

Image of command output

Let's look at using ExtensionData to find the Virtual Hardware version that we just found with Get-View. We'll begin by grabbing the VirtualMachine object for the virtual machine. You may ask why we would want to use Get-VM at all when we could simply use Get-View. That is an excellent question, and it is often discussed, but slightly beyond the scope of this post.

Back to work…

In the code that follows, I illustrate using the Get-VM cmdlet to retrieve the Config property from the object stored in the ExtensionData property. 

$vm = Get-VM "w2k8-std-32-2"

$vm.ExtensionData.Config

The image that is shown here displays the information that is contained in the Config property.

Image of command output

Well, that was easy enough, and now you have direct access to that info and more. The information provided with ExtensionData is exactly the same as what is provided with the Get-View command. However, we now have considerable flexibility for when and where we decide to grab this data.

We can take this one step further and generate a quick one-liner to report on the Virtual Hardware version for all virtual machines in my little lab setup. In the code that follows, notice that we use ExtensionData right in line without skipping a beat. 

Get-VM | Select Name,@{Name="HardwareVersion";Expression={$_.ExtensionData.Config.Version}}

 The command and data returned by that command are shown here.

 Image of command output

Step it Up with New-VIProperty

PowerCLI also provides one more little feature that was introduced in version 4.1 that makes a report like this even easier in larger scripts. Because we know the hardware type is part of the virtual machine, let's make it a direct property of the object with New-VIProperty! Here we're using a value from the ExtensionData property for that VirtualMachine object. This will allow us to simplify our query. The simplified query is shown here. 

New-VIProperty -Name HardwareVersion -ObjectType VirtualMachine -ValueFromExtensionProperty 'Config.Version'

Get-VM | Select Name, HardwareVersion

The command and the output associated by the command are shown here.

Image of command output 

Keep in mind that this new property for the VirtualMachine object is only available while the session is active. If you plan to add new properties by using New-VIProperty, make sure that you include their creation in your scripts. If you don't, you may be left wondering why you don't have any data for that property. Doh! This lack of data is shown in the image that follows.

Image of command output 

This was a simple example of these very PowerShell-ful features from VMware's PowerCLI. I definitely recommend taking some time to explore with Get-View and the ExtensionData property in your environment. You'll find a wealth of information about many objects in the inventory, including VMhosts, virtual machines, resource pools, folders, and more. When you find those valuable tidbits, you can speed up and simplify your scripts even more with the New-ViProperty cmdlet. That's all for now! Good luck and good scripting!

~Josh

Thank you, Josh, for taking the time to share your knowledge with us. Join us tomorrow when I will talk about customizing the Windows PowerShell console. It is a cool blog that you will not want to miss.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Customize the PowerShell Console for Increased Efficiency

$
0
0

Summary: Learn how to easily configure the Windows PowerShell console prompt for more efficiency and increased productivity.

Microsoft Scripting Guy, Ed Wilson, is here. Well, once again it is the weekend. The first ever Pittsburgh PowerShell Users Group meeting was a resounding, sold-out success. The Scripting Wife and I had a great time at the meeting, and we had the chance to see old friends, and to make new friends as well. I predict we will begin to make Pittsburgh one of our frequent haunts—it is a cool town, and the Windows PowerShell Users Group is awesome.

The problem with the PowerShell console

One problem that I have (because I have a dozen computers that I use on a regular basis) is keeping my Windows PowerShell console so that it appears the same. Because I might take screen shots to use in the Hey, Scripting Guy! blog or in a book I might be writing, it is important that the Windows PowerShell console be standardized. In addition, when I make a presentation or teach, the default red error message with the black background does not show up very well with some projectors—and it really does not show up well when printed in black and white.

It is true, that the properties of the Windows PowerShell console are configurable via the application, but that is a manual process, and it is difficult to make it repeatable. The Windows PowerShell console properties box is shown in the image that follows.

Image of dialog box

Tackle the problem of repeatability

In addition to the lack of repeatability, not all of the things that I need to change are available via the Windows PowerShell console properties dialog box. I cannot, for example, configure the color of the error messages. To change items, such as the color of the error messages, requires accessing the Windows PowerShell console settings via Windows PowerShell itself.

For ease of use, I uploaded the Set-PSConsole function to the Scripting Guys Script Repository.

To configure the Windows PowerShell console, I like to access the settings via the $host variable. To get to the user interface portion, I use $host.ui.rawui which contains an instance of an instance of the InternalHostRawUserInterface object.

The foreground color and background color are straightforward value assignments. The two lines of code in my Set-PSConsole function that do this are shown here.

$host.ui.RawUI.ForegroundColor = "black"

$host.ui.RawUI.BackgroundColor = "gray"

Creating a BufferSize object

Next, I need to set the buffer size. The BufferSize property contains an object. The easiest way to obtain access to this object is to query the BufferSize property, and store the resulting object in a variable. It is then a simple matter of assigning new values for the Width and Height properties. When I have modified the properties of the BufferSize object, I need to call the Set_BufferSize method to write the properties back to the Windows PowerShell console. These four lines of code are shown here.

$buffer = $host.ui.RawUI.BufferSize

 $buffer.width = 85

 $buffer.height = 3000

 $host.UI.RawUI.Set_BufferSize($buffer)

Determining the maximum windows size

Now I want to set the window size of the Windows PowerShell console. First, I need to check the maximum window size (based on the current Windows screen resolution). I use the Get_MaxWindowsSize method to obtain the maximum size of the Windows PowerShell console window. I then query the WindowSize property to return a WindowSize object.

Now, I need to perform a few checks. If the width of the maximum window size is greater than or equal to 85, I set the width to 85. If the maximum window size width is less than 85, that is the value I use. I use the same logic for the height. When I have assigned new values to the $ws variable that contains the WindowSize object, I call the Set_WindowSize method to write the values back to the console. These lines of code are shown here.

$maxWS = $host.UI.RawUI.Get_MaxWindowSize()

 $ws = $host.ui.RawUI.WindowSize

 IF($maxws.width -ge 85)

   { $ws.width = 85 }

 ELSE { $ws.width = $maxws.width }

 IF($maxws.height -ge 42)

   { $ws.height = 42 }

 ELSE { $ws.height = $maxws.height }

 $host.ui.RawUI.Set_WindowSize($ws)

Setting the remaining colors

The remaining six lines of code are simple value assignments. I set all of the background colors to white, and choose different foreground colors for the errors, warnings, and verbose messages. Here is the code that sets these six colors.

$host.PrivateData.ErrorBackgroundColor = "white"

 $Host.PrivateData.WarningBackgroundColor = "white"

 $Host.PrivateData.VerboseBackgroundColor = "white"

 $host.PrivateData.ErrorForegroundColor = "red"

 $host.PrivateData.WarningForegroundColor = "DarkGreen"

 $host.PrivateData.VerboseForegroundColor = "DarkBlue"

I add Set-PSConsole to my Windows PowerShell profile, and I call it at the entry point to my profile. This portion of my Windows PowerShell console profile is shown in the image that follows.

Image of profile

The new colors appear in the following image.

Image of profile

I like my new color configuration, but if you don’t, you are free to download Set-PSConsole and create your own color scheme. This is the beauty of Windows PowerShell—if you don’t like something, in general, you can change it.

I hope you have a great weekend. See you tomorrow when I will tackle a problem that I have with my music files and my Zune. The code solved an annoying problem for me—maybe it will help you too. See you then.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Use PowerShell to Easily Organize Your Music Collection

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, teaches you how to use Windows PowerShell to organize your music collection for ease of playback on Windows.

Microsoft Scripting Guy, Ed Wilson, is here. I love the weekend. In general, it is a time for me to explore and to experiment with Windows PowerShell. These experimentation sessions quite often are purpose driven—I have some annoying problem that has finally reached a sufficient threshold of vexation to merit a few time slices of my day.

I enjoy playing music on my computer while I am writing. Often I listen to my Zune, but it is easier for me to use Windows Media player. There is little performance hit on my system because my music files are on a separate drive than my operating system or my Hey, Scripting Guy! data files. The problem with the manual method of playing music files is that the songs reside in dozens of individual, nested folders. This layout is shown in the image that follows.

Image of folder

For manual music operations, a better arrangement (at least for me) is to have all the songs in a single folder. With this arrangement, all I need to do is select the songs I want to listen to, and then I can select Play directly from Windows Explorer. This technique is shown in the following image.

Image of folder

The cool thing about Windows PowerShell is that I can fix annoying problems, such as the one I have with my music files, without the need to write a script. In fact, it is three commands to fix this issue. The first command sets my working location to the G:\music folder. The next command moves all of my music files to the root of the Music folder. I use the Get-ChildItem cmdlet (gci is the alias) to retrieve a listing of all the MP3 files in my Music folder. I use the Foreach-Object cmdlet (% is the alias) to move each file by using the Move-Item cmdlet (move is alias) to the root of my Music folder. Here is the command.

sl g:\music

gci -Filter *.mp3 -recurse | % {Move -Path $_.fullname -dest g:\music}

Note: For my particular application, this technique works just fine. If you have files outside of nested folders, you will need to add a check for the parent folder, or else the command will attempt to move the file to the location where the file already exists, and it generates errors.

If you are not certain what a command will actually do, make sure that you use the WhatIf parameter. Here is the command, revised to incorporate the WhatIf parameter.

gci -Filter *.mp3 -recurse | % {Move -Path $_.fullname -dest g:\music -whatif}

Now, I need to delete all the empty folders. To do this, I use two commands. The first command uses the Get-ChildItem (gci is alias) to find all of the child folders. I use the Recurse parameter to force the command to find folders and all subfolders. To identify a folder, I use the Where-Object (? is the alias) to find the Mode that begins with the letter d. Here is the first command to find all the folders.

$dir = gci g:\music -recurse | ? {$_.mode -match '^d'}

Now I need to find the folders that are empty. I pipe the collection of FolderInfo objects to the Where-Object (? is the alias), and I call the GetFiles method to see how many files exist in each folder. If there are no files, I pipe the object to the Remove-Item (del is the alias) cmdlet. Here is the second command.

$dir | ? { !($_.getfiles().count) } | del

Once again, if you are not certain as to what the command will accomplish, add the WhatIf parameter to the Remove-Item command. Here is the command that uses the WhatIf parameter.

$dir | ? { !($_.getfiles().count) } | del -whatif

Cool, I now have my music collection in a single folder, and I can use Windows Media player to play directly from within Windows Explorer. Sweet! Join me tomorrow when I will welcome Microsoft PowerShell MVP, Sean Kearney, as he begins a special week in Blueville. It is great stuff!!! See you!

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell to Find and Unlock Users in Active Directory

$
0
0

Summary: PowerShell MVP, Sean Kearney, shows how to use Windows PowerShell to find and unlock users in AD DS.

Microsoft Scripting Guy, Ed Wilson, is here. Today I am happy to announce that Honorary Scripting Guy and Microsoft PowerShell MVP, Sean Kearney, is back. This week will be Windows PowerShell in Blueville. Take it away Sean.

Blueville, Inc. was a wonderful place,
Where each IT worker has scripted a pace,
The management of actions, the flowing of work
Made each member function with nary a quirk.

But up in the tower, a mouse at his hands,
Was poor Mr. Finch surveying the lands,
All day with the GUI he stumbled so slow,
Not so productive, his output was low.

But in Blueville, the workers, we’ll call them the Blues
Were dancing and happy, with all of the news
Of a magical system, and from Monad it came,
Called PowerShell you see, such a wonderful name.

Mr. Finch spent each and every day a clicking and grunting,
Each moment you see, on the screen he was hunting.
He looked down from his office, his fingers were drumming,
Deep in his mind, his thoughts were a humming.

“Why are they so happy?” he wondered aloud.
”Why are the Blues smiling?” his thoughts in the cloud.
He watched as they worked with smiles in their eyes,
Their shiny Blue teeth raised to the skies.

Mr. Finch clicked Pause on the Seuss-RhymeOMatic and got up from his chair. He was having a difficult time. Over and over, the words ran through his head.

“…I have a problem that is giving a pain,
A riddling problem that is nagging my brain,
I have a division that is driving me dilly,
The users get locked out daily, it’s making me silly.
They get locked out in twos, and even in bunches.
They lock themselves out while chomping on lunches.
Please give me a way, please do it now
To stop all this madness, please show me how…”

Constantly unlocking accounts for a particular division in Active Directory…what a dilemma. Constantly pulling up the username, clearing the box, going back in for the next one a few moments later. It was driving him goofy.

Those words echoed in his head. He had thousands of happy Blue workers in Blueville, Inc. If only he had learned VBScript script long ago. But it seemed so hard. He decided to take a walk to see the Blues. Sometimes talking things out gave him ideas…always a good way to go.

A little Blue was humming away. It was Stu. Stu Blue. Stu was happily smiling at a screen of Blue and unlocking a user.

Mr. Finch looked on at Stu and queried him too.

”How is it Mister Stu? How is it to work so happily upon that screen of Blue?”

Stu looked up at Mr. Finch and smiled away. “It is PowerShell Mr. Finch, a system that speeds up my day.”

Mr. Finch had heard of PowerShell—from Mount Monad it came, but it was a scripting solution, and all were the same.

Stu looked up and noticed the RhymeOMatic light was on, and quickly hit Pause.

“Thanks! That rhyming was driving me crazy. So I thought Windows PowerShell was another scripting solution? Isn’t it hard to learn?”

Stu thought for a moment. “I never thought about that because I don’t think I learned Windows PowerShell. I just use one or two cmdlets daily. I unlock users.”

This sounded like the person to help. “How hard is it to unlock an account with Windows PowerShell, Stu Blue?”

“I simply key in the cmdlet called Unlock-AddAccount, and the SAM account name of a user, like this.”

Unlock-AddAccount ‘John.Smith’

“And of course, it’s never just one user, sometimes it’s an entire division from Blueville, Inc. It tends to go all thumbs some days….”

Mr. Finch nodded. This sounded familiar, but it must involve scripting. Mr. Finch watched.

“If I need to unlock an entire division of silly people typing on their keyboard with their noses, I can just pull up the list in Active Directory with a Get-ADUser cmdlet like this.

GET-ADUSER –filter * –searchbase ‘CN=Legal,CN=Boston,DC=Blueville,DC=Local’

Mr. Finch watched an entire list of users flow by on the screen.

“Should I wish to unlock them all ‘carte blanche,’ I can simply pipe the content straight into the previous cmdlet like this.”

GET-ADUSER –filter * –searchbase ‘CN=Legal,CN=Boston,DC=Blueville,DC=Local’ | UNLOCK-ADACCOUNT

Mr. Finch looked over at Stu. “How did you learn how to work the cmdlets in Windows PowerShell?”

Stu looked up. “Within Windows PowerShell, there is a beautiful Help system. I key in Get-Help, the cmdlet name, and the Examples parameter. For example, the first time I wanted to learn how to use Unlock-ADAccount, I typed the following…”

GET-HELP UNLOCK-ADACCOUNT –examples

“…and it provided me with some good examples. I know they say you can script with Windows PowerShell, but I am only first-level support. I only use it for unlocking users and it suits me fine!”

Mr. Finch was impressed. He decided to return upstairs with this new knowledge. If basic use of Windows PowerShell was this easy, perhaps scripting wasn’t so hard.

And so Mr. Finch went back to floor two,
Went away to his computer to a screen of pure Blue.
That night PowerShell, he did open to play
And discover the wonder awaiting next day…

Thanks Sean. Please join us tomorrow for Part 2.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell Cmdlets to Search Active Directory

$
0
0

Summary: Windows PowerShell MVP, Sean Kearney, shows how to use Windows PowerShell cmdlets to search Active Directory.

Microsoft Scripting Guy, Ed Wilson, is here. Honorary Scripting Guy and Microsoft PowerShell MVP, Sean Kearney, is back today with Part 2 of PowerShell in Blueville. Take it away Sean…

So the story today, we continue for you,
Is about Mr. Finch, no script he could do,
Never learned in the past, too tricky for he,
Because that vbscript, was not that easy you see.
But yesterday I think, about half past two,
He sat there a chatting, with Mister Stu Blue.
A happy IT worker, Mr. Finch he did sell
A wonderful technology, we call Powershell.
So easy to use, the curve it was low,
Unlocking divisions, off he did go.
Mr. Finch sat now leaning, off on the rail,
Thinking aloud, let’s continue the tale…

He gazed down below at the scampering Blues
As they happily worked in ones and in twos.
No grumbling was made, no groaning was heard,
As they scripted, they work with nary a word.

Perhaps they might know, the answer today
In their happy go lucky fun little way.
A problem that plagued him, and poked him in bed,
A nattering issue that was stuck in his head.

A division was added, to Blueville that day,
But a split of its assets would be carried away.
The controller they had not, and only half of the staff,
Such a stumbler…Mr. Finch had just had to laugh.

“All that typing away! Mistakes could be made!
And security groups too! The settings to wade!”

“Oh if only there was a way…” he sadly would sigh,
“To export all that content, in Excel it could fly.
I could take what I wanted, and then purge all the rest,
Then maybe import it, that would be best.”

Down the rail he did go, he did take a slide
To the Blues working below, oh what a ride!
He ran into a worker, right out of the blue,
Was a network admin, who’s name it was Hugh.

Hugh Blue came out to Mr. Finch holding his hand,
Kicking a RhymeOMatic, right in the can.

“Thanks, I can never think quite clear with those,” said good Mr. Finch. “So I ran in to Stu, who showed me this new technology called Windows PowerShell.”

Hugh nodded. “Yep makes our life far easier. Without it, I’m not sure what we’d be doing.”

Mr. Finch nodded. “So perhaps you might be able to help me out. There is a new division we have acquired, but due to a split of the assets, we will not get the domain controller because only half the assets and people are coming over. They will give us temporary access to the domain controller to pull out data from the OU that contains those users, but I would rather have something cleaner to work with than running an LDIFDE command. Can Windows PowerShell help?”

A smile spread across Hugh’s face. “Piece of cake with Windows PowerShell and the Quest ActiveRoles cmdlets! It is a free tool that I can download from the Internet to manage Active Directory, particularly on older systems!”

Mr. Finch looked on as Hugh entered a line on his computer.

GET-QADUSER –searchroot ‘Blueville.local/Boston/Legal’

“This will give you all the users within the legal OU under Boston in our Active Directory, Mr. Finch…”

Mr. Finch gazed upon the list flowing on the screen. “That looks easy, but will that give me all the information about those users?”

“If you like, I can pull down all the properties for the users, it will take longer of course, but I simply add this parameter.”

GET-QADUSER –searchroot ‘Blueville.local/Boston/Legal’ –includeallproperties

“That’s nice, but I’d really like that in some kind of text file so I could manipulate and cherry pick what I need. Even if it was a raw text file that I could work with in Excel somehow…”

“Excel you say?” Hugh blinked. “You will like this then. Windows PowerShell has a cmdlet called Export-CSV, which will export everything that is sent to it as a comma-separated value file. Perfect for Excel. We just make this one minor change to the line.”

GET-QADUSER –searchroot ‘Blueville.local/Boston/Legal’ –includeallproperties | EXPORT-CSV Users.CSV

In moments, Mr. Finch was staring at an exported spreadsheet of all the users, and he could see fields available, including whether their accounts were disabled or enabled and the dates that they were created. He was impressed, but then a thought struck him.

“Hugh, with my problem I need to pull the users from the system, but I won’t be on their domain. I will have credentials, of course, and the IP address of the server, but I don’t think they’ll let me install Windows PowerShell on their server just to do this.”

“No problem, Mr. Finch! Here is a slight change we can make, and then we can use those credentials on your work laptop as long as the wire is attached. We use another cmdlet called Get-Credential and connect away like this:

$Credentials=GET-CREDENTIAL

Mr. Finch watched as Hugh ran the cmdlet. It produced a standard validation box on the screen.

“For the test domain here, which is NEWCONTOSO, I type this:”

NEWCONTOSO\Administrator

“And of course, my super secret password. And now I put in the IP address of the server and the results from $Credentials in this line:

GET-QADUSER –searchroot ‘NewContoso.local/OtherUsers/OnesWeOwn’ –includeallproperties –service ‘192.168.1.5:389’ –credential $Credentials | EXPORT-CSV Users.CSV

Mr. Finch almost fell back. Not only was Windows PowerShell pulling down the information, but he noted that Hugh was doing it on his Windows 7 Premium laptop, which was not even on the domain.

Hugh saved all the lines into a file called GETTHEUSERS.PS1, and then he stored it and the free software needed to install the Quest cmdlets on Mr. Finch’s laptop. “Call me if you have problems, but this should work well.”

Mr. Finch could not wait to tackle the problem. This Windows PowerShell seemed to make everything easy again!

So waving to Hugh, and out of the door
Mr. Finch ran with a smile, and tore up the floor.
Couldn’t wait to get started on the task and the site,
With PowerShell here, it wouldn’t take all night.

 

Sean continues to share his creativeness with us tomorrow in Part 3.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 


Use PowerShell String Techniques to Create User Names for Active Directory

$
0
0

Summary: Microsoft PowerShell MVP, Sean Kearney, shows how to use Windows PowerShell string techniques to create user names for Active Directory.

Microsoft Scripting Guy, Ed Wilson, is here. It is time for PowerShell in Blueville Part 3 with Honorary Scripting Guy and Windows PowerShell MVP, Sean Kearney. WooHoo…

So where we did go? What did we say?
Oh, right! Mr. Finch was just dancing away!
He had learned from two guys, both ending in Blue,
Two fellows he met called Hugh and called Stu,
Of PowerShell from that Microsoft land,
A beautiful thing, which did gave him a hand.
It unlocked divisions in one line of work,
And exported data with nary a quirk.
So off Mr. Finch did run to the site,
And obtained his user list, not overnight!
Now back in his office, import them he must,
In PowerShell, he was certain this he could trust.
Smart was Mr. Finch, he knew what to do.
He leapt down five floors and consulted a Blue!

With a thud he did land, with thick padded shoe,
Thank goodness he bought them, and so bouncy too.
Leaping down stairs was silly you see,
Unless you had shoes as thick as a tree.
So blinking away and trying to see,
He saw his goal looming, the Blues of Tier three.
He opened the door and saw one alone,
Quickly finish a task on the unified phone.
Smiling away at a table for two,
Was the smartest of all, her name it was Sue.
And before you could say, “Stop that rhyming, please” twice,
She smacked RhymeOMatic with a large bag of rice.

“Hello, Mr. Finch, I’m Sue…Sue Blue.”

Mr. Finch smiled. Somehow he suspected that. “I have a problem but I suspect you’re going to say, ‘With Windows PowerShell, that’s a breeze!’”

“Yes, it probably is, but what do you need to do?”

Mr. Finch showed Sue the exported CSV file from the Quest cmdlets he obtained from the client site. “I cleaned it up so I have only the First and Last names of the users, but I need to import this into an OU I created under the Boston OU called ‘Contoso’. I found a cmdlet called New-QADUser, and most of it makes sense, but I am having problems. First off, where do I start?”

“So what is in your file, Mr. Finch?”

“I have a list of First Names and Last Names of all the staff brought over from Contoso. However, what I would really like to know is…Is there an easy way with Windows PowerShell to build the User IDs in the system? I figured if I could get the new staff in with a simple password, I could update the additional data later. Our UserID is normally a user’s first name followed by their last initial.”

“Absolutely Mr. Finch. For this, we’re going to build a small script to…”

Mr. Finch shuddered. “Scripts. Oh, that’s FAR too difficult for me. I never learned scripting.”

“If you’ll allow me to finish. Let me show you at least how you make that user name in Windows PowerShell. Then we’ll see who can’t script.”

“Let’s show you some basic work with what’s called a variable in Windows PowerShell. If we put a dollar sign ($) before a word in Windows PowerShell, it’s a variable. Windows PowerShell variables can have very descriptive names like $MrFinch or $BlueScreenOfLife or $FriedChicken95BumbleBees. So let us show you how to build a user name. Let us assign a make-believe user to two Windows PowerShell variables.”

$FirstName=’Gazoo’

$LastName=’Blue’

“Let’s imagine our user name was simply the first and last names added together. Within Window PowerShell, if we type the following…”

$FirstName+$Lastname

“…it will show us this as if we added them together:”

GazooBlue

“Now if we want to have Windows PowerShell remember that, we use another variable to remember that.”

$Username=$Firstname+$Lastname

“Now, you try Mr. Finch. Try your name where I used $FirstName and $LastName.”

Mr. Finch tentatively walked up to the keyboard like it was going to bite him. He typed slowly, unsure of what the computer might do to him—like swallow him whole—or worse, work in VisiCalc.

$FirstName=’Mister’

$LastName=’Finch’

$Username=$FirstName+$LastName

“Now watch what happens when I type $Username,” coaxed Sue Blue.

MisterFinch

Mister Finch nearly fell out of his azure blue boxer shorts. It…it just worked! “That wasn’t difficult!” he muttered to himself. “So how could we make that into our standard user name? Is there a way to tell Windows PowerShell to only use the first letter?”

Sue Blue nodded. “Windows PowerShell has built-in features to pull information out exactly the way you want it. We just add ‘substring()’ to the end of the Windows Powershell variable, and we tell it how much we want out.”

“Here’s an example. This will show us all the letters from the word starting at position 0 (zero), which is the first position, and it will return three letters.” Sue Blue keyed into screen as the results returned.

$LastName.substring(0,3)

Fin

“Oh!” burst out Mr. Finch, taking initiative. “So if I only wanted the first character, which would be position 0 and only ONE character it would be:”

$Lastname.substring(0,1)

“Which means…I think it means…to make my user name, I do believe this would work.”

He carefully typed in the console, no longer frightened, but interested and excited.

$Username=$Firstname+$Lastname.substring(0,1)

Mr. Finch nervously watched as he typed in $Username and saw:

MisterF

“WoooHoooooo!!!! Did I just script?”

Sue Blue smiled. “You will if you put that into Notepad and save it with a file name and .PS1 as the extension.”

Quickly Mr. Finch keyed in:

$Firstname=’Mister’
$Lastname=’Finch’
$Username=$Firstname+$Lastname.substring(0,1)
$Username

Then he saved it as NEWUSER.PS1.

“Now Mr. Finch, I believe I shall guide you down to tier 4, where I believe that the next Blue can help you along.”

It wasn’t so hard, he couldn’t believe,
Scripting seemed easy, oh such a reprieve.
He just couldn’t wait to get started again
In moments in minutes with another Blue friend.

That is all for Part 3. Please join us tomorrow as Sean continues life in Blueville.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell to Read a CSV file and Create Active Directory User Accounts

$
0
0

Summary: Learn how to use Windows PowerShell to read a CSV file and to create new user accounts in Active Directory.

Microsoft Scripting Guy, Ed Wilson, is here. Once again we return to Windows PowerShell Blueville with guest blogger Sean Kearney.

Mr. Finch was just smiling, a man full of glee,
For he had just left Sue, Miss Blue on Tier 3.
He learned something small, but learned it quite quick,
”This PowerShell thing is really quite slick!”
He learned how to turn two names into one,
And discovered it all, it felt so much fun.
So ventured he now, along that shiny Blue floor
To the chambers of the wizards that managed Tier 4.

On the way further down, he managed to see
A gaggle of Blues that were chanting so free,
Singing PowerShell songs, carols into the night,
Blueville IT workers, oh what a sight.
They sang high like the gerbils, shrilly into the air,
Oh their singing it was horrid, but they just didn’t care,
For PowerShell made them full of passion and joy,
Every single Blueville worker, each girl and each boy.

So quietly down and with nary a squeak,
Mr. Finch snuck away from their loud noisy shriek.
He admired their passion and their love of the Shell,
But Oh, did their voices sound like a daemon from hell.

So soon Mr. Finch found and opened the door
To the wizards and masters within that Tier 4.
Awaiting for them after the buzzer he rang,
Soon creaking open, locks moved with a klang.

“Please stop just a bit, before you continue your crimes,
We see you are followed by something that rhymes,
It drives us all mad, it makes us go crazy,
This rhyming machine, it makes our mind hazy.
Please stop that dumb thing, or cover it now,
We’re begging you please, don’t have a cow.”

But before he could stop it and get them some peace,
the batteries fell out and rolled down a crease.

“Oh thank goodness! I do not know who keeps bring these stupid RhymeOMatics in here! They’re driving us nuts!”

Mr. Finch looked up into the air, whistling quietly. Bad idea he approved from the social committee. He was not fessing up today.

“So! I must ask good gents what are your names?

Two hands burst out. “Hi my name is Lou!” the first mouth announced.

“…and I am Bo….”

Mr. Finch interrupted him. “Boo? Is your Name Boo?”

The second voice shook it’s head. “Uhhh no. Bouregard is  my name, but if you like, you can call me Boo. I kinda like that for a nickname.” He smiled, “Boo Blue…cool!”

“So how can we help you, our tortured soul of the rhyme?”

Mr. Finch quickly explained how he needed to import a list of users in a CSV file, how he had exported it from an old Active Directory, and then cleaned it up to just having the First and Last names.

“Sue Blue showed me how to build those names to match my Login account format, but at this point, I need to know how to pull that list in.”

Lou looked up. “Well you remember you used Export-CSV to pull the data out? There is another cmdlet called Import-CSV to read the data. To read your data, we just type in…”

IMPORT-CSV C:\Imports\Users.csv

Mr. Finch was impressed as he watched all the data flow by from his file. That part was remarkably easy.

“And now we’ll show you how to step through that information. We’ll store it all away in a Windows PowerShell variable called $UserList like this:”

$UserList=IMPORT-CSV C:\Imports\Users.csv

Mr. Finch was puzzled for a moment. “I’m a little confused. How can a Windows PowerShell variable hold something as simple as a First name or as complex as that entire list with no changes?”

Boo Blue stepped in. “That’s because all information in Windows PowerShell is an object. To you, you just saw the name—but to Windows PowerShell, it was an object that had not only a length, but content and methods available to it. Windows PowerShell doesn’t care about what an object has, just that everything is an object. Do not worry too much about them now. My head spun around about three times when I first tried to think too hard about an object.”

Mr. Finch nodded. After he learned how to import the users, he would look deeper into this whole “object” thing.

“So!” Lou popped in “We have your entire file in the Windows PowerShell variable $UserList. If we wish to step through this list, we simply use the ForEach-Object cmdlet. There are two ways that we can do this. One is to pipe the information directly into ForEach-Object like this:

$USERLIST | FOREACH-OBJECT { $_ }

“This will echo each entry in the list to the screen.”

“So what is that funny Windows PowerShell variable? The one with the underscore for a name?”

“AhHa! Glad you caught that!” piped up Boo. “That’s one of the many built in automatic variables that Windows PowerShell has. It contains the current object in the pipeline. As we stepped through the information in $UserList, this holds the entire content of that line. Or we can do it this way:

FOREACH ($Person in $UserList) { $ Person }

“In the second instance (using the $Person in $UserList setup), the variable $Person replaces the automatic variable, and it gives us something far more readable. Both work equally well, but I prefer the latter for readability. So if you’d like to access each entry in the list, we reference the names of the columns in your CSV file like this:”

FOREACH ($Person in $UserList) {
$Person.FirstName
$Person.LastName
}

As he watched the names roll by, Mr. Finch commented “I noticed that you moved the parentheses apart. Can they be on different lines?”

Boo jumped in, “Yes, they can sir. The Windows PowerShell parser looks for certain characters to tell it where the beginning and ending of a script block is. When it sees the first parentheses, it says ‘AhHa! Code is starting here!’ and it presumes that everything is code until it finds the opposite parentheses.”

“Ahhhhh….” Mr. Finch suddenly lit up like a light bulb. “Hey! Does that mean I can work with this variable the same way Sue Blue showed me? For building names?” He quickly grabbed the script Sue had him save and opened its content.

$Firstname=’Mister’
$Lastname=’Finch’
$Username=$Firstname+$Lastname.substring(0,1)
$Username

“So if I put these changes into your Windows Powershell script block like this…”

FOREACH ($Person in $UserList) {
$Person.FirstName
$Person.LastName

$Username=$Person.Firstname+$Person.Lastname.substring(0,1)
$Username
}

As he ran the script and he saw the names and account names flow down the screen, Mr. Finch felt a revelation. “Scripting really isn’t all that hard! So if this is all we need to do to get the names, how do we create a new user? I remember reading up on using New-QADUser. I got help by using the following script:”

GET-HELP NEW-QADUSER –examples

“…and when I ran it using this command line…”

NEW-QADUSER –firstname John –lastname Smith –userpassword ‘TempPass1’ –ParentContainer ‘Blueville.local/Division/Contoso’

“I found information like the SamAccountName and my UserPrincipalName weren’t there. Did I do something wrong?”

“Nope, you just needed to give it a little more information. You have to build your UPN for the Quest cmdlet. It will typically be in the format of username@domain.local. To find the @domain.local for your system, pull up an existing user account and look for the part after @. When you have that, you can modify your script to do the work for you.

I know that our domain is@blueville.local, so to have the script do that work, we make this change. Then we run the New-QADUser cmdlet with the proper parameter. Your entire script with the original import added would look like this.”

# Import list of Users From CSV into $Userlist

$UserList=IMPORT-CSV C:\Imports\Users.csv

# Step through Each Item in the List

FOREACH ($Person in $UserList) {

# Build Username from First name and Last Initial

$Username=$Person.Firstname+$Person.Lastname.substring(0,1)

# Put our Domain name into a Placeholder, who wants all that typing?

$Domain=’@blueville.local’

# Build the User Principal Name Username with Domain added to it

$UPN=$Username+$Domain

# Create the Displayname

$Name=$Person.Firstname+” “+$Person.Lastname

# Create User in Active Directory

NEW-QADUSER –FirstName $Person.Firstname –Lastname $Person.Lastname –Name $DisplayName $Name –SamAccountName $Username –UserPassword ‘1NewPassword’ –UserPrincipalName $UPN –Name $Name –ParentContainer ‘Blueville.local/Division/Contoso’

}

“Let’s save this as a new script called IMPORTNEWBLUE.PS1. Also, if you notice, there are a lot of lines in the script that start with a hash character. Those are all comments to help us remember why and how we did what we did. To execute the script, we just run it as.”

/IMPORTNEWBLUE.PS1

Mr. Finch watched the console. The list of all the staff was being effortlessly imported into the system. A large smile beamed across his face. “Scripting is coooooooolllll!” A puddle of drool formed from his mouth.

Lou and Boo looked at each other and smiled. The power of PowerShell was already flowing into Mr. Finch, his teeth were turning blue. His eyes were lit up like theirs.

“WooHoo! Thank you Lou and Boo,” he burst out, patting each on the back. “Now I can’t WAIT to get back to work! You’ve saved me hours of time!”

Mr. Finch spun about and danced out the door skidding across the hall towards his office.

“Look out world here I come!”

What a shout and a hoot came about in the air,
Mr. Finch full of joy and he hadn’t care.
For so much work in such simple way
He just couldn’t wait for PowerShell next day.

That is all for Part 4. Please join us tomorrow for the finale at Blueville.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell to Audit Active Directory User Account Creation

$
0
0

Summary: Microsoft PowerShell MVP, Sean Kearney, shows how to use Windows PowerShell to audit account creation in Active Directory.

Microsoft Scripting Guy, Ed Wilson, is here. Now, with the exciting conclusion to Windows PowerShell Blueville, here is Microsoft PowerShell MVP, Sean Kearney.

So hear me all now, and hear me all true,
The lessons that came, from the workers all Blue,
All five of them told, of that I am sure,
Too many to count, so many they were.

First there was Stu, we saw in one line,
Unlocked whole divisions, in barely no time.

Then we had Hugh, who as quick as can be,
Showed how to store users in one CSV.

Don’t forget Sue, a spark in her eye,
Showed how to use strings right on the fly.

Oh, then Boo and then Lou, right down on Tier 4,
Showed how to create en masse, and oh so much more.

So now Mr. Finch was about to sit down,
And play with that PowerShell, such like a clown,
When a BAM came away upon his wood door,
A BAM so darn hard it rumbled the floor.

He opened the door to go take a peek,
Pushing it slowly with barely a creak.

When in burst a team, briefcases in hand,
Of auditors…nine from compliance land.

“We come from Frank Orgen and Xylonite now,
For your quartlerly SOX,” as one took a bow.
”We need a report before we have lunch,
Of all your new users, and not in a bunch.
Please format it nice, please keep it neat,
CSV output cannot be beat,
All new staff you hired from April ‘til June,
Please hurry up, you have until noon.

They marched out that door, with no moments to spare.
Those Nazguls of audit, leaving foul rancid air.

“Oh no, can it be? At ten in the morn?
F.O.X here for SOX?” He felt so forlorn.

But then Mr. Finch, had a thought like a light,
”Get them Blues up here! They’ll make this take flight!
If PowerShell could solve so much in a fly,
Perhaps it could be used for this, worth a try!”

And so he rang down, to the Blues down below,
To call up his friends, all five don’t ya know?

They arrived up in moments, like huns from Ben Hur,
To battle the task, they moved like a whir .
But they all had to pause, with a twitch and a quirk,
”This rhyming must stop before we can work!”

And so a few moments were spent with a crate,
Filling rhyming machines, sealing their fate.
It was bolted right shut and topped with a bow,
”It should look nice!” said Sue, oh don’t ya know?

“I’ll get those out of here by courier,” said Mr. Finch. “Maybe ship them to a company that makes pads from fruit.”

“So Mr. Finch,” popped up Sue. “What’s the dilly?”

“Auditors!” he burst out, exploding on the wall. “Auditors! They want a report of all the new staff in Active Directory!”

“…and?” queried Stu, “where’s the challenge?”

“You do know that’s piece of cake,” offered Lou.

“….in Windows PowerShell,” completed Boo.

“Yes, we can help you do that. That’s easier than you can imagine!” brightened Hugh.

Mr. Finch paused and breathed a sigh. Somehow he suspected they would say something like that. He smiled. “So where do we start?”

“First we need to see if there are any fields with the word Date available in Active Directory. Hopefully there should be one or a few. I’m pretty certain it stamps the date of object creation in everything in Active Directory. I’ll pick on Lou for this,” piped up Boo.

GET-QADUSER ‘Lou Blue’ | SELECT-Object *date*

They looked and noticed two fields appearing from Active Directory: ModificationDate and CreationDate. “Is there any way to compare this with the current date easily?” queried Mr. Finch.

“Sure is,” offered Stu. “In Windows PowerShell, there is a cmdlet called Get-Date, which by default, will give us the current date and time. But all we need to compare for you are two specific dates: April 1, 2011 and June 30, 2011. This is the range for your quarter. There are a lot of ways that we can set up this information, but we can use GET-DATE and Windows PowerShell variables to store that away. For April, we enter the following:”

$BeginDate=GET-DATE ‘4/1/2011’

Mr. Finch looked up “Oh! And would this work for the ending date?” He typed:

$EndDate=GET-DATE ‘6/30/2011’

They all smiled. Mr. Finch was getting the hang of this. “But we should set the ending date to July 1 so we can collect all new staff that were created on June 30 as well.”

$EndDate=GET-DATE ‘7/1/2011’

“Next we obtain a list of Users in your Active Directory. For that, we run the Get-QADUser cmdlet just like before, and we’ll store the information in a variable.”

$OurUsers=GET-QADUSER

Hugh looked up. “There are many ways we can do this, but we can use the Where-Object cmdlet. It allows you to filter information that is coming from other cmdlets. To compare the CreationDate to see if it is later than or greater than the $BeginDate, we would type:

$OurUsers | WHERE-Object { $_.CreationDate –gt $BeginDate }

A stream of all users that were created since April 1, 2011 flowed down the screen.

“As you can see we can compare by using –gt which means ‘greater than.’ We can use –lt, which means ‘less than’ to compare with the other date,” offered Boo.

$OurUsers | WHERE-Object { $_.CreationDate –lt $EndDate}

Mr. Finch scratched his head. “OK, I can almost see the picture, but how do we tie it together?”

“We can use the operator –and like this:”

$OurUsers | WHERE-Object { $_.CreationDate –gt $BeginDate –and $_.CreationDate –lt $EndDate }

A much smaller list appeared on the screen. It was all the staff from that quarter.

“Waaaaait a minute. The auditors wanted this in a CSV format. Would this work the same way as when I exported the users from Contoso?” Mr. Finch Wondered aloud.

$OurUsers | WHERE-Object { $_.CreationDate –gt $BeginDate –and $_.CreationDate –lt $EndDate } | EXPORT-CSV C:\Audit\NewStaff.csv

He was opening the CSV file to verify the data. He sorted on the CreationDate in Excel, and sure enough, it was all staff that was created between April 1, 2011 and July 1, 2011. He even noted that it showed the new user created on June 30, 2011 at 11:45pm.

“So how could I save this for regular use? This seems like something I would love to have handy.”

Sue jumped in, “Just key the lines into any text editor and save them with an extension of .PS1”

Mr. Finch grouped all of the following lines together:

$BeginDate=GET-DATE ‘4/1/2011’
$EndDate=GET-DATE ‘7/1/2011’
$OurUsers=GET-QADUSER
$OurUsers | WHERE-Object { $_.CreationDate –gt $BeginDate –and $_.CreationDate –lt $EndDate } | EXPORT-CSV C:\Audit\NewStaff.csv

He quickly saved them as a file called SOX.PS1. “This will be handy the next time they pop around,” he smiled with glee.

No sooner had they finished the report and copied it to a USB key when the door opened and the nine auditors from F.O.X. walked in.

“Do you have our SOX?” asked the auditors from F.O.X.

Mr. Finch and the Blues smiled, handing them the USB key with the needed data. The nine spun about as quickly as they had entered.

“And now my friends…the best way I can thank you all. Now that we are rid of F.O.X. and the SOX, would you please take this crate of RhymeOMatics and smash them on some rocks?”

The five blues laughed with glee, hauling the box of irritating things away, patting Mr. Finch on the back as they did.

Mr. Finch looked online quickly to purchase a book about Windows PowerShell. He quickly ordered “PowerShell Step-by-Step” to get him going.

And so Mr. Finch was off on his way,
Learning more scripting each little day.
With the help of new friends, those wonderful Blues,
He learned to use PowerShell, without any dues.
It could do in one line, the work needed be,
Or possibly script in two or in three.
So on that day, Mr. Finch moved down unto
The far place below, with everyone Blue.
In a land full of screens of blue gleaming bright,
He worked long with the Blues into the night.
Learning and spreading the PowerShell way,
He went home with a smile each every day.

Thank you, Sean, for such a great week at Blueville.

Join us tomorrow as Gary Siepser starts the weekend as our guest blogger. It is cool.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Use Windows Search to Find Your PowerShell Scripts

$
0
0

Summary: Microsoft PFE, Gary Siepser, shares a cool Windows Search trick to find Windows PowerShell scripts.

Microsoft Scripting Guy, Ed Wilson, is here. Today our guest blogger is Gary Siepser. Here is Gary to tell us about himself:

Photo of Gary Siepser

I am a premier field engineer(PFE) in the Customer Services and Support(CSS) part of Microsoft Services. Wow, I know that is a mouthful, so let me lay it out a little more simply. I am a field engineer who helps premier support customers in getting the most of their Microsoft infrastructure. There are several flavors of PFEs, but I am what we refer to as "transactional,” and I work with many customers with a limited technology focus. I used to be a messaging PFE and support Exchange Server (I even managed to become a Microsoft Certified Master in Exchange 2007), but I have morphed over to 100% Windows PowerShell support engagements with our customers. These engagements are mostly delivering premier workshops.

Note: If you are premier customer, we have some great Windows PowerShell instructors and workshops, so talk to your technical account manager (TAM) to find out more. If you are not a premier customer, why not?

I also work with our customers beyond workshops, such as custom-scoped, onsite Windows PowerShell knowledge transfer. This is typically built around a scripting need that a customer has, and I provide knowledge transfer and sample solutions to help customers solve those needs. When I am not working with customers, I am the U.S. PowerShell tech lead within our PFE organization, and I focus a lot of attention on IP development, internal readiness, Train-the-Trainer (TTT) delivery and instructor recruitment, and generally promoting Windows PowerShell throughout PFE and by proxy to our customers.

Blog: Gary's $this and that about PowerShell and Exchange

You can use Windows Search to easily find your scripts by searching for anything in the scripts. This post covers a simple topic, but it is an important one that is helpful to me, and hopefully to you. First of all, old versions aside, I think that Windows Search in Windows 7 works really well. It is very fast at finding files, documents, communications (like emails), and more. Let us focus on using it to help find Windows PowerShell scripts.

Windows Search will also find your scripts if you have you have them stored in a location that it is indexing. You can see the indexed locations on your Windows 7 computer by looking in the Control Panel under Indexing Options. The first thing we want to do is ensure that our scripts are in a location that is set up to be indexed. The screenshot below shows the main Indexing Options window and the Indexed Locations window that pops up when you click the Modify button on the initial window.

Image of Windows window

You can see that you can set what locations are indexed by simply checking the box next to the folder in the tree view. You will find the most common locations where Windows intends for you to store files that are already indexed (such as your user profile folders). I normally keep my documents and user files in the profile folders where I think Windows expects them. However, in the case of my scripts, I break my normal rule and I store them in a folder on my secondary hard drive (D:). I ensure that the location in my D drive is selected. I would suggest that you select the locations where you keep any files you want to be able to more easily find (like your scripts folder if it is not already selected).

Ensuring that your scripts folder is indexed is valuable, but it is only a piece of the “hotness.” The real key is to now tell Windows Search to index the actual contents of your Windows PowerShell scripts. Yes, that means Windows Search will read your scripts and let you search (very quickly) for anything in your scripts (think a function or cmdlet name).

To set this, you need to get a little deeper into the indexing options. You need to click the Advanced button on the main window. If you use user account control (UAC) and depend on its settings, you might have to approve the security prompt.

When the new Advanced Options window opens, click the File Types tab. On this tab, you want to ensure that you tell Windows Search to index the properties and file contents of your scripts. This most certainly means .ps1 files, and I would also include .psm1 and .psd1 files to cover script modules and the module manifest respectively.

In the following screenshot, I have highlighted the three lines where I edited the default settings of the properties, and I have them set to Plain Text Filter (which is the Index Properties and File Contents radio button when each file extension is selected). When you OK the change, Windows Search will rebuild your index (it takes a little time, but it’s a background task that is respectful of activity on your system).

Image of Windows window

When your index is up-to-date with the locations and file contents of your scripts, you can find your scripts easily. You can leverage the search in many places; for example, on the main Start menu or when a file window is open (like in the following ISE open file window). Now you can search on anything in the scripts. For example, you could search on a cmdlet name and easily see which scripts contain that cmdlet. Or you could search on some particular comments that you add to your scripts. Of course, some folks have better “Search Kung Fu” than others, so your mileage may vary. In the following screenshot, I show how I can leverage the search when I am trying to open a script through the Windows PowerShell ISE.

Image of Windows window

In the previous screenshot, you can see that I typed export-csv as my search string (highlighted in yellow). The results include scripts that do not have that in their name, but it appears in their content. This trick really helps me almost on a daily basis because I do not focus intently on how I name my scripts or how I organize my script folders.

With this feature, I am able keep a flat folder structure with only a few folders, but the bulk of my scripts are stored in my main scripting folder. I hope this tip helps you—it really has made a difference for me. Unfortunately, every time I upgrade to a new machine or reinstall Windows, I have to reset these settings. I guess these setting must not transfer with Windows Easy Transfer because I use that tool.

Thank you, Gary, for sharing a very useful tip to help to manage Windows PowerShell scripts.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell and ASCII to Create Folders with Letters

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, shows how to use Windows PowerShell and ASCII characters to automatically create folders with letters in the name.

Microsoft Scripting Guy, Ed Wilson, is here. I recently had a discussion with June and Kim, two of the other Windows PowerShell writers on my team, about creating some training for writers on other teams. Instead of focusing on things that are of general interest to IT Pros—things such as creating users in Active Directory, finding locked out users, or detailing group memberships for specific users—we decided that the Windows PowerShell examples should be of immediate practical use to the writers.

If writers spend several hours from their busy days to learn Windows PowerShell, they should be able to make efficiency gains to not only recoup the hours spent in training, but to receive additional free time as well. To a large extent, it is up to individual writers to see how they can improve their jobs and to find productivity boosts. After all, each writer’s style is different, and each writer’s method of work is self-inspired. As for me, I use lots of folders to organize my projects into manageable pieces. In the past, this meant spending a great deal of time creating folders—now I use Windows PowerShell to do the work.

Automatically creating folders

Using Windows PowerShell to create folders is not difficult. It can be as easy as piping an array of numbers to a Foreach-Object cmdlet that calls the New-Item cmdlet to create the folders. In the following command, I use the range operator to create an array of numbers from 1 through 20. I pipe the numbers to the Foreach-Object cmdlet (% is the alias). Inside the script block of the Foreach-Object cmdlet, I use the md alias to create a new directory. The alias md refers to the mkdir function. The mkdir function wraps the New-Item cmdlet to make it easier to create new directories. I rely on the expanding string (double quotation marks) to create the twenty folders. Here is the code that I use to create my twenty folders.

1..20 | % {md "Chapter$_"}

The command to create twenty folders, and the output associated with that command is shown in the following image.

Image of command output

Automatically creating folders with letters

As we have just seen, creating a collection of folders with numbers in the name is really easy. But what if I need to create a bunch of folders that use letters instead? For example, a book typically has Appendix A and not Appendix 1. In the past, creating various folders for the appendix was a pain (not the least of which was properly spelling appendix). Then one day, it dawned on me…I can use ASCII characters.

We need to first explore ASCII characters in Windows PowerShell…

Create an ASCII table

A long time ago (and I mean like printing with a nine-pin, dot-matrix printer on green-bar, tractor-fed paper), I used to be very good with the ASCII character codes. Today, I seldom use them. In the good old days, I had an ASCII table pinned on the wall in my office. Today, I would have to do a BING search to find a decent ASCII table—if it were not for Windows PowerShell. By using my range operator favorite trick, printing an ASCII table is very simple. In the code that follows, I use the range operator to create the numbers 0 through 127. I use the Foreach-Object (% is the alias) to use each number that comes across the pipeline. Inside the script block for the Foreach-Object cmdlet, I use parameter substitution, the –f­ operator to display the number, and the ASCII character value associated with each number. The [char] class converts the number to ASCII.

0..127 | % {“{0}  {1}” –f $_, [char]$_}

The image that follows displays some of the output from the previous command.

Image of command output

Create folders with letters by using ASCII

Now that I know the ASCII values for letters, it is a simple matter to revisit the original code that created the folders. This time, instead of starting the range operator at 1, I will begin at 65. In the code that follows, I create numbers ranging from 65 through 70. I pipe the number to the Foreach-Object cmdlet (% is the alias). Inside the script block for the Foreach-Object cmdlet, I use the md alias (md is the alias for the mkdir function) to create directories. I build up the name by using Appendix as the prefix for each new directory. I use parameter substitution to add the letters A through F to the end of each folder name. The value that I substitute uses the [char] class to convert a number to an ASCII character. The command is shown here.

65..70 | % {md ("Appendix{0}" -f [char]$_)}

The command to create folders with letter names, along with the associated output are shown in the following image.

Image of command output

The technique of combining the range operator and ASCII values is not limited to automatically creating folders with letter names. I hope you will tuck this trick into your stocking of Windows PowerShell goodies. Join me tomorrow when Microsoft MVP, Sean Kearney, shifts gears by kicking off a week of Windows PowerShell and MDT. It is a great series of blog posts; you do not want to miss it. See ya.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Viewing all 3333 articles
Browse latest View live