Author Archives: ScriptingGuys

Scripting Wife Comments on Beginner Event 1


Summary: The Scripting Wife reveals her impressions of 2013 Scripting Games Beginner Event 1.

Hi everyone, I want to welcome you to the 2013 Scripting Games. This year the events were designed by the Windows PowerShell community, and the games are being run by the Windows PowerShell community. It is a very exciting and important year.

OK, so at first Beginner Event 1 did not make too much sense to me, so I had to read it multiple times before it dawned on me what was going on. You see, I am not a computer guru—I am more at home with Microsoft Excel than with Windows PowerShell. But Windows PowerShell is cool, and I have come to appreciate it over the years. In fact, I have found that some tasks are actually easier to do from Windows PowerShell than from the GUI (this is especially true for me in Windows 8).

Anyway, at first when I read this event, I was thinking event logs…hmmmm…I think I need to use Get-EventLog. I looked up the Help on Get-EventLog by doing the following:

help Get-EventLog

I looked through everything, but I did not find what I needed. Then it dawned on me that just because something says it is a log, does not mean it goes into an event log. These log files are really just files with a .log extension. I can treat them like they were text files. And I know how to work with text files.

Then I decided to look at some Hey, Scripting Guy! Blog posts about working with files for storage. I came up with this list of blogs. Next I decided to look at blogs that talk about working with dates and times, and I came up with this list of blogs.

By using these resources, I was able to come up with a solution to the Beginner Event 1. I spent a couple of hours reading and reviewing as part of the event, and I thought it was a fun process. I hope you will join in the spirit of the 2013 Scripting Games. You will be glad you do!

~Scripting Wife

2013 Scripting Games: Advanced Event 1


Summary: Dr. Scripto needs to gather all log files older than 90 days and move them to an archive location.

Microsoft Scripting Guy, Ed Wilson, is here. This is a day for the history books. Today is the first day of the 2013 Scripting Games as run by the Windows PowerShell community at www.powershell.org. Of course I am in full support mode for the Scripting Games, and I will also participate as a judge. Without further ado, here is Advanced Event 1…

An Archival Atrocity

Dr. Scripto is in a tizzy! It seems that someone has allowed a series of application log files to pile up for around two years, and they’re starting to put the pinch on free disk space on a server. Your job is to help get the old files off to a new location.

Actually, this happened last week, too. You might as well create a tool to do the archiving.

The current set of log files are located in C:\Application\Log. There are three applications that write logs here, and each uses its own subfolder. For example, C:\Application\Log\App1, C:\Application\Log\OtherApp, and C:\Application\Log\ThisAppAlso. Within those subfolders, the filenames are random GUIDs with a .log filename extension. After they are created on disk, the files are never touched again by the applications.

Remember that the Scripting Games are being hosted at www.powershell.org, so head on over there to submit your entry.

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

Ed Wilson, Microsoft Scripting Guy 

2013 Scripting Games: Beginner Event 1


Summary: Dr. Scripto needs to gather all log files older than 90 days and move them to an archive location.

Microsoft Scripting Guy, Ed Wilson, is here. This is a day for the history books. Today is the first day of the 2013 Scripting Games as run by the Windows PowerShell community at www.powershell.org. Of course I am in full support mode for the Scripting Games, and I will also participate as a judge. The Scripting Wife is all set and ready to start on the Beginner Events. Without further ado, here is Beginner Event 1…

An Archival Atrocity

Dr. Scripto is in a tizzy! It seems that someone has allowed a series of application log files to pile up for around two years, and they’re starting to put the pinch on free disk space on a server. Your job is to help get the old files off to a new location.

The log files are located in C:\Application\Log. There are three applications that write logs here, and each uses its own subfolder. For example, C:\Application\Log\App1, C:\Application\Log\OtherApp, and C:\Application\Log\ThisAppAlso. Within those subfolders, the filenames are random GUIDs with a .log filename extension. After they are created on disk, the files are never touched again by the applications.

Your goal is to grab all of the files older than 90 days and move them to \\NASServer\Archives. You need to maintain the subfolder structure, so that files from C:\Application\Log\App1 get moved to \\NASServer\Archives\App1, and so forth.

Remember that the Scripting Games are being hosted at www.powershell.org, so head on over there to submit your entry.

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

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell to Get Name and IP Address of Virtual Machines


Summary: The Scripting Wife learns to use Windows PowerShell to get networking information from all virtual machines running Hyper-V in prep for the 2013 Scripting Games.

Microsoft Scripting Guy, Ed Wilson, is here. It is the last day of the Windows PowerShell summit held in Redmond, Washington. One hundred Windows PowerShell gurus, enthusiasts, and MVPs from several countries have come together on the Microsoft campus to network and learn about the coolest Windows PowerShell ever. For the Scripting Wife and I, this will be a very, very long day. After a full day at the Windows PowerShell summit, I have a meeting with some teammates, and the Scripting Wife and I head to SeaTac for the red-eye flight back to Charlotte. Unfortunately, these days, it is nearly impossible for me to sleep on airplanes. Between the cramped seats, constant jostling, and random flashlight seatbelt checks, I just resign myself to try to get a bit of work done.

I am once again sitting in the lobby, sipping a cup of hot tea and thinking about making the most of our last day here with our friends, when along comes the Scripting Wife. She plops down in the seat beside me, and before I can say anything, she begins…

“I need to be able to find out some information from a Hyper-V server,” she exclaimed.

“My aren’t you becoming a geekette,” I said.

“Can it ,Script Monkey! I think I will need to know something about Hyper-V for the 2013 Scripting Games,” she said.

Use PowerShell to obtain basic Hyper-V information

“The good thing is that my laptop is running Hyper-V with Windows 8, and I installed the management module, so I can help you. What do you want to know?” I asked.

“Well for one thing, I think I want to know the names of the virtual machines, and maybe some networking stuff…I guess,” she said.

“Well that is pretty easy,” I said. “You can use the Get-VM Windows PowerShell cmdlet.”

The Scripting Wife did not hesitate, she immediately typed Get-VM and began to peruse the output. The command is shown here.

Get-Vm

“If you need to work with a single virtual machine, you can use the name to select just that virtual machine. To do that use the Name parameter,” I said. “Go ahead and return only the C1 virtual machine.”

“That makes sense,” she said as she began to type. Here is what she typed:

Get-VM<space>-n<tab>c1<enter>

The command is shown here:

Get-Vm -Name c1

The command and the associated output from the command is shown in the image that follows.

Image of command output 

Finding additional virtual machine info

“Suppose I want to find out additional information about the virtual machine?” she asked.

“Well, you can pipe the virtual machine object to the Format-List cmdlet to see the other properties,” I said.

The Scripting Wife used the Up arrow to retrieve her previous command, and she added a pipeline and typed Format-List and a wildcard character. Here is what she typed:

<up arrow><space>|<space>Format-L<tab><space>*<enter>

The command is shown here.

Get-VM -Name c1 | Format-List *

“What is that NetworkAdapters property?” she asked.

“That property contains an additional object. To get inside the object, use the Select-Object cmdlet and the ExpandProperty parameter,” I said.

The Scripting Wife used the Up arrow and retrieved the previous command. She then erased the Format-List * portion of the command. She then added a pipeline character, used Select-Object, and expanded the NetworkAdapters property. The following is what she typed:

<up arrow><space>|<space>Select<space>-exp<tab><space>networkadapters<enter>

The command she created is shown here.

Get-VM -Name c1 | Select -ExpandProperty networkadapters

The command and the output are shown here.

PS C:\> Get-VM -Name c1 | Select -ExpandProperty networkadapters

Name IsManagementOs VMName SwitchName MacAddress Status IPAddresses

—- ————– —— ———- ———- —— ———–

Network Adapter False c1 InternalSwitch 00155D003002 {Ok} {192.168…

“Well, that is pretty cool. The problem is that I only need the virtual machine name, the switch, mac address, and the IP address. Do I add the property parameter like I normally do with the Select-Object cmdlet?” she asked.

“Go ahead and try it,” I suggested.

When she typed the command, and pressed ENTER, the screen became covered with errors. The command and the associated errors are shown here.

Image of command output 

“Well that didn’t work,” she laughed.

“Yeah, I sort of wish it would, but it don’t. What you need to do is add a second Select-Object command and choose your properties,” I said.

The Scripting Wife thought for a few seconds, and began to type. The following is the command she created.

get-vm -Name c1 | select -ExpandProperty networkadapters | select vmname, macaddress, switchname, ipaddresses

“And I can do the same thing if I want this information from all of the virtual machines?” she asked.

“Absolutely. Why don’t you try it?” I said.

She thought for about a minute, then she pressed the Up arrow and retrieved her previous command. She then removed the –name c1 portion of the command and pressed ENTER. Here is the command she created.

get-vm | select -ExpandProperty networkadapters | select vmname, macaddress, switchname, ipaddresses

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

 Image of command output

“That is about all I need,” she said. “Make sure you get us checked out before you head to the summit,” she instructed.

I started to reply, but she was gone. When Windows PowerShell is involved, she is quick.

Join me tomorrow when the first events in the 2013 Scripting Games appear. WooHoo! The Scripting Games are here, the Scripting Games are here…almost. See you tomorrow.

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

Ed Wilson, Microsoft Scripting Guy

Use the AD DS Module to Find Computers with PowerShell


Summary: The Scripting Wife learns about querying Active Directory Domain Services with Windows PowerShell in prep for the 2013 Scripting Games.

Microsoft Scripting Guy, Ed Wilson, is here. Well yesterday I gave my first presentation (of three) at the PowerShell Summit happening here in Redmond, Washington. I thought my talk went really well, and several tweets I saw seemed to confirm that. I was also able to see some excellent presentations during the say. The Scripting Wife ran the registration desk, and so she was able to see everyone who is out here this week.

I am still on Charlotte time, and therefore am waking up at oh dark thirty each day. Surprisingly, the Scripting Wife has also been making early morning appearances for breakfast as well. Anyway, I am in the lobby sipping a cup of English Breakfast Tea (I did not bring any lemon grass or cinnamon sticks with me) and so it not my favorite cup of tea ever – but it is ok. It is strong, and helps to breakdown the croissants.

My eyes are closed as I visualize my two presentations I am making today at the PowerShell Summit. I open my eyes, and there she is – the Scripting Wife.

“If you are that sleepy, you need to go back upstairs and get some rest,” she began.

“Not sleepy, I am thinking about my presentations today,” I said.

“Oh that. Then you are not really doing anything, and so you can help me,” she said.

“What do you need,” I asked.

“I need to know how to query Active Directory,” she said.

“Say what,” I shockingly exclaimed. “Why do you need to query AD?”

“Well I think it is going to be in the 2013 Scripting Games, and so I figure I need to know how to query AD,” she said with emphasis.

“Well all right,” I said.

Use the ActiveDirectory module to query AD

“First let me fire up a Windows Server 2012 domain controller on my Windows 8 Hyper-V,” I said.

“I will take your word for it,” she said will as little interest as possible. “Just let me know when you are ready.”

I logged onto the Windows Server 2012 domain controller, and opened the Windows PowerShell console.

“Ok. It is up,” I said.

“So what do I need to do,” she asked.

“Well to get a listing of all computers in AD use the Get-ADComputer cmdlet. Specify a wildcard for the filter,” I instructed.

The Scripting Wife slid the laptop over to her, and typed the following:

Get-ADCo<tab><space>-f<tab><space>*<enter>

When she typed –f<tab> there was a slight pause, and the Windows PowerShell progress bar appeared as it imported the ActiveDirectory module. After that, the command proceeded quickly. The command appears here:

Get-ADComputer -Filter *

A command and the first computer from the output appears here:

PS C:\> Get-ADComputer -Filter *

DistinguishedName : CN=DC1,OU=Domain Controllers,DC=nwtraders,DC=msft

DNSHostName : dc1.nwtraders.msft

Enabled : True

Name : DC1

ObjectClass : computer

ObjectGUID : e1b57333-7155-4026-949d-82c35400a850

SamAccountName : DC1$

SID : S-1-5-21-1844339390-1396565537-2470583527-1001

UserPrincipalName :

Requesting a specific attribute

“Well that was painless. But what if I need to know the version of the operating system. How do I get that information? It does not seem to be in the output,” she asked.

“Well that is a very good observation. There are many more properties for each object in Active Directory Domain Services than are returned by a basic query. The reason for returning a subset of the attributes is for performance reasons. To request a specific property such as operatingsystem add the properties parameter. Why don’t you go ahead and try that,” I suggested.

The Scripting Wife thought for about 30 seconds and then she used the up arrow to retrieve her previous command. Next she added the –properties parameter and she added the operatingsystem attribute. This is what she typed:

<up arrow><space>-p<tab><space>operatingsystem<enter>

The command she created appears here.

Get-ADComputer -Filter * -Properties operatingsystem

The command and first output appears here.

PS C:\> Get-ADComputer -Filter * -Properties operatingsystem

DistinguishedName : CN=DC1,OU=Domain Controllers,DC=nwtraders,DC=msft

DNSHostName : dc1.nwtraders.msft

Enabled : True

Name : DC1

ObjectClass : computer

ObjectGUID : e1b57333-7155-4026-949d-82c35400a850

OperatingSystem : Windows Server 2012 Standard

SamAccountName : DC1$

SID : S-1-5-21-1844339390-1396565537-2470583527-1001

UserPrincipalName :

Using wildcards for properties

“That is pretty cool, but I do not like the output – it is rather crowded. I only want the name of the server and the name of the operating system. Can I get that,” she asked.

“But of course you can,” I said in my best Bela Lugosi imitation.

Ignoring the humorous voice, she plowed on, “So are you going to help me?”

“Well you cannot use a wildcard in the properties parameter, but when you use the Sort-Object cmdlet and the select-Object cmdlet you can use wildcards. So retrieve your previous command and pipeline it to Sort-Object and to Select-Object,” I said.

She thought for a minute, and this is what she typed:

<up arrow><space>|<space>sort<space>oper*<space>|<space>select<space>name,oper*<enter>

The command she created appears here:

Get-ADComputer -Filter * -Properties operatingsystem | sort oper* | select name, oper*

The command and the output appears here:

PS C:\> Get-ADComputer -Filter * -Properties operatingsystem | sort oper* | select name, oper*

name OperatingSystem

—- —————

C7 Windows 7 Ultimate

C1 Windows 8 Enterprise

C2 Windows 8 Enterprise

SQL1 Windows Server 2012 Datacenter

DC1 Windows Server 2012 Standard

“That is pretty cool. Thanks. I am outta here, I think I just saw some PowerShell Summit people come in,” she said. And with that she was gone. I figured I would see her again when I got to building 40.

Join us tomorrow as the Scripting Wife continues studying for the 2013 Scripting Games.

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

Ed Wilson, Microsoft Scripting Guy

2013 PowerShell Scripting Games Official Kickoff


Summary: It is finally here…the 2013 Windows PowerShell Scripting Games official kickoff

Microsoft Scripting Guy, Ed Wilson, is here. It’s here. It’s here. It’s here. Yep, the 2013 Windows PowerShell Scripting Games are finally here, and today is the official kickoff. Launched this morning at the first community organized International Windows PowerShell Summit taking place in Redmond, Washington this week, the 2013 Scripting Games launched amid great fanfare!

This year, the Windows PowerShell community is running the games, and they are being spread out over a longer period of time. This week we are in Redmond, Washington for the PowerShell Summit, and last night we had the welcome aboard meet and greet. While at the meet and greet the Scripting Wife was clowning around with Jason Helmick flexing their muscles getting ready for the Scripting Games.

clip_image002

What you need to know about the 2013 Scripting Games

The first thing you need to know about the 2013 Scripting Games is that the entire point of the games is to learn Windows PowerShell. This is absolutely your best chance of learning Windows PowerShell this year. The exercises are based upon real life scenarios, and so you will learn skills you will immediately be able to use. The deadlines for each event provide added incentive towards completing the scenario. The peer pressure helps provide incentive towards meeting the goals of each exercise.

Second, the 2013 Scripting Games are fun. The community involvement via Twitter, Facebook, blogs, and forums all provide interaction and opportunities to share your knowledge and your experiences.

Third, the 2013 Scripting Games provide you with time to focus on your Windows PowerShell skills. During the games, dozens of scripters will be blogging about the events, lessons learned, and hints for success. Because the games are based on real world scenarios, this becomes a huge opportunity for picking up useful tools.

The Scripting Wife has already been honing her Windows PowerShell skills in preparation for the 2013 Scripting Games. She will also be blogging about her experiences with the games. There is a special RSS feed just for the Scripting Games, so it will be easy to keep up with all the activity.

Fourth, the 2013 Scripting Games take place over a six-week period of time. This will provide you with more time between events, the judges more time for grading the scripts, and the bloggers more time to keep up with the event. So you will not have to take vacation this year to keep with all the events.

Fifth, the 2013 Scripting Games are hosted on PowerShell.org, so this is the place to go to so you can find the particulars of registration, the competitors pack, and associated information.

Sixth, register for the 2013 Scripting Games. The first event is in three days!

Join me tomorrow when the Scripting Wife and I talk about using the Active Directory module with Windows PowerShell.

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

Ed Wilson, Microsoft Scripting Guy

Use PowerShell to Modify WMI Data Such as Drive Labels


Summary: The Scripting Wife learns how to use the CIM cmdlets and Windows PowerShell to assign a new drive label by modifying WMI data.

Weekend Scripter: Changing WMI information

Microsoft Scripting Guy, Ed Wilson, is here. Well yesterday, after we got through an extremely long security line at the airport, and we jiggled around in a very cramped airplane for over five hours on our flight to Seattle for the Windows PowerShell summit, we met up with Windows PowerShell MVP Jeff Wouters at the SEATAC airport. We gave him a ride into Redmond and spent some time hanging out. Here is a picture of Jeff and the Scripting Wife I took near the Seattle Space Needle. You see, Jeff has never been to Seattle, and so we thought we would show him around.

clip_image002

The Windows PowerShell summit has already been a success, in my mind, and the first session has not even kicked off. Of course, the biggest thing for the summit for me is not the sessions, but the chance to talk and to interact with members of the community. In this regard, the summit has already exceeded expectations.

I decided to head down to the lobby of the hotel to grab something to eat this morning. It is still very early for people from the West Coast, but for the Scripting Wife and I it is already late due to the three time zone change for us. Of course, for our European friends the change is twice as great. I am sipping a cup of English Breakfast tea, munching on a croissant, and checking my email, when a loud thudding sound accompanied by a sudden shaking of the table woke me from my revelry–it is the Scripting Wife.

I hazard a query, “Why are you are up so early?”

“Hey, it is nearly noon back in Charlotte–I am ready to go,” she chirped.

“Go? Go where?” I ventured.

“Out. We are getting together a group, and we are heading out,” she explained.

“Where is out? Have you no specific destination in mind?” I asked.

“Well, maybe downtown Seattle. There is great shopping there, a cool aquarium, ferry terminals, and wonderful restaurants. We are in a real city my friend, and I for one am not going to let the opportunity pass,” she pontificated.

“I can appreciate that. But before you go, let me show you something you might need to know for the 2013 Scripting Games,” I said.

“OK, but make it quick, because when the group shows up, I am out of here,” she said.

Using the CIM cmdlets to set a WMI property

I opened the Windows PowerShell console with admin rights by right clicking the Windows PowerShell console icon and selecting Run As Administrator from the action menu. Admin rights are required to modify WMI objects.

“OK. Look over here,” I directed. “Find WMI classes related to disks.”

Find disk classes

The Scripting Wife thought for a few minutes, and typed the following.

Get-Cimc<tab><space>*disk*<enter>

The command she typed appears here.

Get-CimClass *disk*

Get dynamic classes

“Now, narrow your output to only dynamic WMI classes,” I instructed.

She used the Up arrow to retrieve the previous command and she added the Dynamic qualifier to her command. Here is what she typed:

<up arrow><space>-q<tab>dynamic<enter>

The command looks like this when complete.

Get-CimClass *disk* -QualifierName dynamic

Get logical disk info

“Cool. Now what we want to do is to find out information about drive C. To do this use the Get-CimInstance cmdlet and retrieve information about the Win32_LogicalDisk WMI class,” I said.

The Scripting Wife thought for a second, and then she began to type. Here is what she did:

Get-CimI<tab><space>Win32_LogicalDisk<enter>

The command looks like the following:

Get-CimInstance win32_logicaldisk

“OK, now look at the output and see if you can tell the property that indicates that it references the drive names,” I said.

She looked at the laptop screen. The output looks like the following:

PS C:\> Get-CimInstance win32_logicaldisk

DeviceID DriveType ProviderName VolumeName Size FreeSpace

——– ——— ———— ———- —- ———

C: 3 159486308352 10707682…

D: 3 System Reserved 366997504 113971200

F: 3 ExtraDisk 499738734592 43558884…

Narrow down the drives by letter

“So you did a good job. You found information about all of the logical disks. What I want you to do now, is to return only drive C . To do that you will need to use the Filter property,” I said.

It did not take the Scripting Wife long at all before she used the Up arrow to retrieve her previous command and to add the Filter parameter. Here is what she typed.

<up arrow><space>-f<tab><space>’DeviceID=’c:’”<enter>

Here is the command she created.

Get-CimInstance win32_logicaldisk -Filter “deviceid=’c:'”

The command and output from the command are shown here:

PS C:\> Get-CimInstance win32_logicaldisk -Filter “deviceid=’c:'”

DeviceID DriveType ProviderName VolumeName Size FreeSpace

——– ——— ———— ———- —- ———

C: 3 159486308352 10707628…

Setting a WMI property

“Notice that you have no label, or VolumeName,” I said. “To set that you will use the Set-CimInstance cmdlet and use a hash table for the Property parameter,” I said.

“Huh?” she asked. “You know that I do not speak geek.”

“It makes more sense when you just do it,” I said.

“Well, hurry up. I just got an email on my phone and they will be here in a few minutes,” she said.

“OK. First of all, you need to use the Up arrow and retrieve your previous command. Then you need to pipe the command to the Set-CimInstance cmdlet. Do that, but do not press ENTER because there is more to the command than that,” I said.

The Scripting Wife thought for a few minutes, and then began typing. Here is what she typed.

<Up Arrow><space>Set-Cimi<tab>

She paused.

“OK, now you need to add the Property parameter and an ampersand and an opening and a closing curly bracket. Do not press ENTER,” I said.

This time, she did not hesitate. Here is what she typed:

-p<tab><space>@{}

“Good. Now inside the two curly brackets you add your ‘property name equals new value’ statement. Here, you are going to set a value for the VolumeName. Give it a value of SSD_Drive,” I said.

Here is what she typed:

volumename=’SSD_Drive’

“Awesome. You are nearly there. Add the PassThru parameter so the modified WMI object returns to the Windows PowerShell line, and press ENTER,” I said.

Here is what she typed:

-p<tab><enter>

The complete command is shown here. (This is a one-line command,broken at the pipe so it is more readable.)

Get-CimInstance -ClassName win32_Logicaldisk -Filter “deviceid=’c:'” |

Set-CimInstance -Property @{volumename=’SSD_Drive’} –PassThru

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

clip_image004

 

“Well, isnt that special,” she mocked.

“Actually, I think it is pretty cool,” I said somewhat defensively.

“I do too. I was just giving you a hard time,” She said.

At that instant, the automatic doors to the lobby swung open and in walked a couple of obvious PowerShellers. I could tell by the T-shirts, but the Scripting Wife obviously knew them.

“Well, I am outta here. Have fun working on your presentation,” she said.

“So when will I see you again?” I asked.

“We’re meeting Don Jones and others at Azteca at 5:00,” she said.

“Zulu?”

Without missing a beat, she said “No. Pacific Standard Time.” And she was gone.

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

Ed Wilson, Microsoft Scripting Guy

Weekend Scripter: Use PowerShell to Find the Version of Windows


Summary: The Scripting Wife learns about using Windows PowerShell to find computer hardware information in prep for the 2013 Scripting Games.

Microsoft Scripting Guy, Ed Wilson, is here. This morning did not start with a nice leisurely cup of tea on the lanai at home. Instead, it started by rushing around to get packed, loading up the vehicle, vying for a parking place at the airport, taking a shuttle from parking to the Charlotte terminal, standing in line for an hour for a security screen that was more intimate than my last physical exam, putting shoes and belt back on and repacking my suitcase, and standing in another really long line to buy an overpriced cup of warm milk with a shot of acidic coffee in it. Then another long line to board a really, really cramped airplane. Luckily, the Scripting Wife is with me, and she helps make the entire process a little more pleasant. Yep, we are finally on our way to Seattle for the Windows PowerShell Summit.

Scripting Wife uses WMI

After takeoff, I open my laptop and the Windows PowerShell console and start playing around. Before long the Scripting Wife is poking me in my shoulder.

“Yes,” I say.

“So I think this would be the perfect time for you to talk to me about using WMI with Windows PowerShell,” she stated.

“OK, good idea,” I said as I slid my laptop over to allow her to use it.

“So what now?” she asked.

“Well the first thing to know about WMI is that there are lots of WMI classes. They are also arranged into different namespaces, but we will not get into that right now. To find WMI classes, use the Get-CimClass cmdlet in Windows PowerShell 3.0,” I said.

“Well, that makes sense. So what do I do if I need to find the name of my operating system?” she asked.

“Why don’t you try it? Use the Get-CimClass cmdlet, and put os into a pair of wildcard characters,” I suggested.

She typed the following:

Get-Cimc<tab><Space>*os*<enter>

The command is shown here:

Get-CimClass *os*

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

Image of command output

“Well, that did not do what I expected it to do,” she said.

“So, you have found out that WMI does not really have a class named OS, or anything similar to OS. What are you really looking for?” I asked.

“I want to know the version of Windows,” she said.

“The cool thing about Get-CimClass is that you can search for classes that contain a specific property. So why don’t you look for WMI classes that contain a property of version?” I suggested.

The Scripting Wife typed the following:

Get-Cimc<tab><space>-p<tab><space>version<enter>

The following is the command she created.

Get-CimClass -PropertyName version

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

Image of command output

“It is still a lot of stuff,” she complained.

“Well one of the things you need to know about WMI is that there are abstract and dynamic classes,” I began.

“And why do I need to know that?” she interrupted.

“Maybe you do not need to know all that, but keep in mind you want to use dynamic classes,” I began again.

“Why?” she asked.

“Because dynamic WMI classes go off and get information. So they are the ones that you want to use. The abstract classes are more used by developers,” I said.

“Well OK. So I only need to remember to use dynamic classes. I got it. Now what?” she said.

“The Get-CimClass cmdlet has a QualifierName parameter. You can specify that you only want dynamic WMI classes returned. It will give you a better output,” I said.

“Cool. So let’s do it,” she said energetically.

“Use the Up arrow to retrieve your previous command. Go to the end of the command and add the QualifierName parameter,” I said. “And don’t forget to use tab completion to help you.”

The Scripting Wife typed:

<uparrow><space>-q<tab><space>dynamic<enter>

Here is the command she created:

Get-CimClass -PropertyName version -QualifierName dynamic

The command and associated output are shown here.

Image of command output

“Cool,” she said. “So where do I find the version?”

“Look at the output. See the Win32_OperatingSystem?” I started.

“Oh yeah, I get it,” she said.

“Now use Get-CimInstance to retrieve the operating system information,” I said.

She typed the following:

Get-Cimi<tab><space>Win32_Op<tab><enter>

Here is the command:

Get-CimInstance Win32_OperatingSystem

Here is the command and the output from the command:

Image of command output

“Well ain’t that just special,” she said as she pushed the laptop back over to me. She then opened her Surface and began reading a book that she had downloaded.

I returned to my Windows PowerShell console, and tried to make sense of life.

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

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell to Copy Files and Folders to a New Location


Summary: The Scripting Wife learns about using Windows PowerShell to copy files and folders in prep for the 2013 Scripting Games.

The days seem to start early, and the evening arrives later this time of year in Charlotte, North Carolina. I am sitting on the lanai drinking a cup of English Breakfast tea, and checking my email with my Microsoft Surface RT device. In my mind, it is a perfect way to ease in to the day, and my Surface is great for stuff like checking email and sending tweets.

I hear a sort of a thump, thump, thump, rattle, rattle, rattle sound and I look up. Strangely enough, I see nothing, so I go back to reading my email. But the sound persists. I get up from my swing and go into the house. I see the source of the noise: the Scripting Wife is digging through the front closet, and she has boxes, books, and other assorted paraphernalia spread out over the vestibule floor. In situations like this, I have learned to keep my mouth shut, and to wait for a natural opening.  

“I can’t find the flippin’ book,” the Scripting Wife exclaimed without looking up.

Not sure if this is a question, a remark, or even if it was directed at me, I decide to keep quiet.

“Well are you going to help?” she asked as she stood up.

“How can I help you, my peach?” I implored.

“You can help me find my autographed copy of Don’s book.”

I wait, knowing there is more to come.

“I saw your blog post about the upcoming 2013 Scripting Games, and I need to get back into shape by writing some code,” she stated.

“You are sort of like a scripting butterfly. You go here and there in random directions looking for nectar, but you never light for very long,” I paused to catch my breath.

“But I don’t need to know how to write scripts when I have my very own script monkey. Besides I just do this for fun,” she said.

“Well, I have no idea where you put your book, but I can help you for a few minutes if you wish,” I offered.

“I saw you reading your email, and I did not want to disturb you,” she said.

“Then why all the noise?” I asked.

“Maybe I figured if you came in here, then you were no longer reading your email and I could get you to help,” she posited.

“So what do you need to do?” I asked.

“Tell me about copying files. I have a bunch of pictures on my laptop that I want to copy up to the SAN so they do not get lost,” she said.

“Grab your laptop and come out to the lanai,” I instructed.

Using Copy-Item to copy files

“To copy a file from one folder to another, what cmdlet do you think you might use?” I asked.

Copy-File,” she posited.

“Close. It is called Copy-Item,” I said.

“Oh, yeah. Now I remember,” she said.

“Open your Windows PowerShell console, and use Copy-Item to copy any file from your C:\fso folder to your C:\fsox folder,” I said.

She thought for about a second and typed the following until she had a file selected:

Copy-I<tab><space>-p<tab><space>c:\fs<tab>\<tab> 

Next she typed the following:

-d<tab><space>c:\fsox\mylog.log<enter>

The completed command is shown here:

Copy-Item -Path C:\fso\20110314.log -Destination c:\fsox\mylog.log

When she pressed ENTER, an error arose. The command and the error message are shown in the following image.

Image of error message

“Well, that did not work,” she said, “I guess I do need to go find Don’s book after all.”

“No, I wanted to show you what happens if the destination folder does not exist,” I said.

“How can I avoid that problem?”

“You can use the Test-Path cmdlet. Type Test-Path and supply the path to the folder,” I said.

She typed the following into the Windows PowerShell console:

Test-P<tab><space>C:\fsox

The command is shown here:

Test-Path c:\fsox

The command and the output from the command are shown in the image that follows.

Image of command output

“So the command says False. I guess that means the folder does not exist?” she asked.

“Yep. That is right. Now use the MD command and give it a path so that you create the new C:\fsox folder,” I said.

She typed the following:

MD<space>C:\fsox<enter>

The command is shown here.

md c:\fsox

The command and the output from the command are shown in the image that follows.

Image of command output

“Now use the Up arrow and retrieve your previous Copy-Item command,” I said.

The Scripting Wife pressed the Up arrow a few times until the following command appeared:

Copy-Item -Path C:\fso\20110314.log -Destination c:\fsox\mylog.log

When the command appeared in the Windows PowerShell console window, she pressed ENTER to run the command. Nothing came back.

Easily copy multiple files by using Copy-Item

“That is OK, but I specifically want to copy my pictures, and I do not want to type a bunch of names. So how can I make Windows PowerShell copy my pictures for me?” she asked.

“To do that, use the Filter parameter and specify .jpg, which is the extension you use for your pictures. You will also need to specify the Recurse parameter,” I said.

She typed the following:

Copy-i<tab><space> -p<tab><space>c:\fso<space>-fi<tab><space>*.jpg<space> -d<tab><space> c:\fsox<space>-r<tab><enter>

The command is shown here:

Copy-Item -Path C:\fso -Filter *.jpg -Destination c:\fsox –Recurse

The newly copied files are shown in the following image.

Image of menu

“Cool,” she said as she bounded through the front door.

Scripting Games Prep Week will continue tomorrow when I will talk about Windows Management Instrumentation and obtaining basic computer information.

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

Ed Wilson, Microsoft Scripting Guy 

Advanced Practice for 2013 Scripting Games


Summary: Prepare for the Advanced category of the 2013 Scripting Games with this practice event.

Instead of a PowerTip today, we bring you Don Jones from www.powershell.org and the practice event.

Scripting Games logo

This is a practice event. You will not submit this event to the actual Scripting Games, and no official judging will be available for this event. If you would like to post your solution for peer review, please do so in The Scripting Games forum on PowerShell.org or in another venue of your choosing. It is your responsibility to solicit peer reviews from the community; no judges or moderators are scheduled to provide reviews for this practice event.

Image of Dr. Scripto

Dr. Scripto is in a quandary. On one hand, he has a request to calculate the current uptime for a set of servers in his domain. On the other hand, he’d love to go see the new Michael Bay movie. Query uptime, or watch exploding robots?

Easy choice. While Dr. Scripto enjoys a wonderful motion picture, you get to figure out the uptime thing.

You need to write a command that accepts one or more computer names by means of a parameter. That parameter must accept direct input or piped-in string values. Your command must prompt for computer names if none are provided when the command is run.

Your command output must include each computer’s name (even if you were given an IP address, you must display the name) and the number of hours, minutes, and seconds the computer has been online since its last restart. Your output should consist of four properties with hours, minutes, and seconds broken out into their own individual properties.

When your command runs, it should optionally display each computer name or IP address that it is about to connect to.

You can be assured that all of the servers in the list are in the domain, and that you’ll have authority to query them. They’re all running Windows Server 2008 R2 or later, and they all have Windows PowerShell 3.0 installed. There are no firewalls or other blockages between you and those computers, and your command can assume that all of the servers are online and reachable.

However, if an error occurs connecting to a computer, you should display an appropriate error message that includes the failed computer name or IP address. Your command must also provide a switch that causes failed computer names or IP addresses to be logged to a specified text file. If your command is run without that switch, no logging should occur.

If someone so chooses to do so, they should be able to pipe the output of your command to a CSV file, XML file, or HTML file—but you do not need to provide those features in the command you write.

Hint   There’s a commonly-used WMI class that contains the most recent computer start-up time. There are also event log entries related to total uptime.

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

Ed Wilson, Microsoft Scripting Guy

Beginner Practice for 2013 Scripting Games


Microsoft Scripting Guy, Ed Wilson, is here.  Today we have Don Jones, from www.powershell.org and a practice event for the Beginner category of the 2013 Scripting Games.

Scripting Games logo

This is a practice event. You will not submit this event to the actual Scripting Games, and no official judging will be available for this event. If you would like to post your solution for peer review, please do so in The Scripting Games forum on PowerShell.org or in another venue of your choosing. It is your responsibility to solicit peer reviews from the community; no judges or moderators are scheduled to provide reviews for this practice event.

Image of Dr. Scripto

Dr. Scripto is in a quandary. On one hand, he has a request to calculate the current uptime for a set of servers in his domain. On the other hand, he’d love to go see the new Michael Bay movie. Query uptime, or watch exploding robots?

Easy choice. While Dr. Scripto enjoys a wonderful motion picture, you get to figure out the uptime thing.

You’ve been given a list of server names and IP addresses in a text file, C:\Servers.txt. It contains one name or IP address per line. You need to write a command that displays each computer’s name (even if you were given an IP address, you must display the name) and the number of hours, minutes, and seconds the computer has been online since its last restart. Your output should consist of four properties with hours, minutes, and seconds broken out into their own individual properties.

You can be assured that all of the servers in the list are in the domain, and that you’ll have authority to query them. They’re all running Windows Server 2008 R2 or later, and they all have Windows PowerShell 3.0 installed. There are no firewalls or other blockages between you and those computers, and your command can assume that all of the servers are online and reachable. (If one isn’t, an appropriate error message should display, but it need not contain the failed computer name or IP address.)

Your command should be as concise as possible—a one-liner, if you think you can do it, although you’re welcome to use full command and parameter names for better readability. The output of whatever command you write must enable someone to pipe the output to a CSV file, XML file, or HTML file, if they so choose; but you do not need to provide those functions in the command you write.

Hint   There’s a commonly-used WMI class that contains the most recent computer start-up time. There are also event log entries related to total uptime.

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

Ed Wilson, Microsoft Scripting Guy 

2013 Scripting Games Competitor’s Guide


Summary: The 2013 Windows PowerShell Scripting Games Competitor’s Guide is posted today.

Microsoft Scripting Guy, Ed Wilson, is here. Don Jones from PowerShell.org is back with us today with the 2013 Scripting Games Competitor’s Guide.

Scripting Games logo

 

Take it away, Don…

Welcome to the 2013 Scripting Games, now managed by PowerShell.org! This guide is designed to help you understand how the games work and what you can expect.

Important   See yesterday’s blog, 2013 Scripting Games Start April 22!, for information about the events, tracks, voting, and prizes.

Register

First up, you’ll need to register for the Games by logging on to http://scriptinggames.org/. Note that there is no “www” on this URL. When you register, you’ll be asked to select a track. This is a one-time decision, and you may choose from:

  • None: You don’t want to compete, but you do want to grade other folks’ work by voting.
  • Beginner: You’ll be able to vote on all events, but you will be able to submit only one entry per event in the Beginner track.
  • Advanced: You’ll be able to vote on all events, but you will be able to submit only one entry per event in the Advanced track.

Caution   We ask that you not include any personally identifiable information in your entries. This includes your name, email address, or other contact information.

Games guidelines

Now, these are just ideas for you to consider. They’re not rules, and our judges won’t universally agree on every one of the ideas. So don’t think of this as a magic checklist that will make you win. You don’t even need to force yourself to do all of these things—but keep them in mind.

  • We love to see error handling, especially in the Advanced events. But we don’t like error suppression. That is, if an error occurs, we should see the error, or you should be doing something about it like logging it or displaying an alternate message.
    The exception: it’s okay to suppress an error that means “everything is going fine.” For example, you try to delete a file that doesn’t exist, and you get an error message. Well, that’s fine. At the end of the day, the file isn’t there, and that’s what you wanted. If you do choose to handle an error, make sure you’re doing so intelligently. Don’t wrap your entire code in a big Try{} block. Only wrap the commands that you anticipate will have an error, and Catch{} (and handle) that error.
  • We hate seeing people do more work than is necessary. For example, you shouldn’t ever prompt the user for a missing parameter. Use the Parameter() decorator in Windows PowerShell to mark the parameter as mandatory.
  • We love self-documenting scripts. That doesn’t just mean comments, although those are pretty awesome, too—especially comment-based Help. Using stuff like full command names and full parameter names (and no positional parameters) makes a script more self-documenting.
    Other examples are less obvious. Here’s one: if you’re accepting input arguments, do so via declared, named parameters, not by using the $args collection. $ComputerName (a named parameter) is a lot more meaningful than $args.
  • We hate inconsistency. In whatever you write, try to be consistent with what’s already in Windows PowerShell. A parameter named ComputerName is great; a parameter named ComputerNameS is not great. Nothing else in Windows PowerShell uses the plural, so you shouldn’t either.
  • We love scripts and commands that output objects and not formatted text. That means you shouldn’t embed a Format cmdlet in your script, nor should you use Write-Host in most cases. Exceptions are made by the name of your script or command; for example, we’d expect a command named Show-Info to show information on the screen, implying Write-Host is being used. A command named Get-Something, on the other hand, should output unformatted objects. We’ll pipe those to a Format command ourselves if we want them formatted.
  • We hate scripts that are hard to read. With scripts (as opposed to one-liners), focus on neat formatting. Avoid the backtick (`) as a line continuation trick because that little bugger is hard to see.
  • We love elegance. For example, a lot of folks think the expression “$computer cannot be reached” is easier and less complex than (“{0} cannot be reached” –f $computer)—even though these two expressions produce the same result. Both of those are prettier than ($computer + “ cannot be reached”). Just keep that in mind.
  • We hate redundancy…well, not in server farms, but definitely in script. For example, is all this really needed: Get-Service | Select-Object –Property @{n=’Name';e={$PSItem.Name}}? Could you simply go for Get-Service | Select-Object –Property Name and get the same effect with less typing? We’re not saying this kind of thing will land you on a judges Worst list, but we know it’ll keep you off some judges Best lists.
  • We love functions that use [CmdletBinding()] and take advantage of the features it enables. For example, if you’re manually setting something like $VerbosePreference or $DebugPreference at the top of your script, you’re missing the point.
  • We hate scripts that pollute the global scope. It’s not yours. We don’t track mud into your house, don’t litter up our global scope with your variables.
    Exception: if you write a script module that politely adds global “preference” variables and then politely removes them when we unload it, that’s cool.

Don’t over obsess

In some event scenarios, we’ll give you an example of the expected output. It’s an example, not a mandatory deliverable. Basically, if you have all the right property names and the values look legitimate, you’re fine. They don’t need to be in the order we list them, and they don’t need to exhibit the exact same formatting, unless the event scenario calls out a specific requirement. In many cases, the example output is formatted to fit in a PDF file. It’s going to look different in the Windows PowerShell console, and we’re cool with that.

However, make sure that you meet the scenario requirements. If a requirement says to display a value in gigabytes, you’d better do it. Expect your Crowd Score to be pretty low if you display in bytes when the scenario explicitly asked for gigabytes.

Let’s be clear on something: if your goal going into the Games is to get on every judge’s Best list, you’re playing for the wrong reasons. If your goal is to get an amazing Crowd Score in every event, you’re probably going to be disappointed. This is a learning event.

Here’s another way to think about it: you’re going to have your script reviewed by dozens of peers, and possibly get some expert feedback from some of the biggest names in the industry. That alone is worth participating. The prizes are just icing on the cake!

DO NOT treat the previous list of guidelines as some kind of secret checklist for winning. It isn’t. They’re pretty good ideas in general, but they’re not the only good ideas. Like all rules they come with their own exceptions.

DO obsess about finding a clever, elegant, well-written solution to each problem. Knock our socks off! 

Try to not miss the point

Something different about the Games this year: You’ll notice that the only score you’ll receive is from your peers in the community—and we can’t tell them how to score you. Heck, some of them may not feel they’re even qualified to hand out scores, and a few of those might be right (more on that in a moment).

The score is nice—but it isn’t the point of the Games. The point is to think of creative approaches to real-world problems and to implement those approaches the best way you can.

Our expert judges will be picking their Best and Worst lists to highlight creativity, attention to detail, and overall sk1llz. Every judge will have different opinions, preferences, and techniques; and they’re all purely subjective. We’re not giving them any guidelines. So there’s no secret checklist for winning.

Our judges will blog about what they like and don’t like. And that is the real point of the Games: to learn.

Thanks to our presenting sponsors

Generous support from our sponsors makes the Games and all of PowerShell.org possible.
Please offer them your support and thanks.

Images of sponsor icons

GOOD LUCK!

~Don

Thank you to Don Jones and to the rest of the Windows PowerShell community for sharing this. Join me tomorrow when we will reveal two practice events for the 2013 Scripting Games.

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

Ed Wilson, Microsoft Scripting Guy 

2013 Scripting Games Start April 22!


Summary: Announcing the Windows PowerShell 2013 Scripting Games, which begin April 22, 2013.

Microsoft Scripting Guy, Ed Wilson, is here. Don Jones is with us today from the offices of PowerShell.org. Tell us what you know about the 2013 Windows PowerShell Scripting Games, Don…

The Games are coming! With an all new software platform (kindly provided by Start-Automating.com, an organization that actually uses Windows PowerShell to power our website), we’re ready to roll. You’ll find Beginner and Advanced tracks, with some new twists.

Events and tracks

Events are open only for a limited period of time. Each event will go through four phases. Dates refer to midnight GMT of the indicated day (meaning, Feb. 2 would be “Feb. 2 at midnight GMT”). The four phases are:

  • Pending: The event is not yet open.
  • Open: You can download the event scenario (as a PDF for offline use) and submit entries. You usually get about five days to do this.
  • Review: No new entries are accepted, but everyone can vote on all events.
  • Public: No entries or votes are accepted, but anyone (logged on or not) can see the entries.

Remember, the Scripting Games are now managed by PowerShell.org, a community-owned corporation that runs the PowerShell.org website. The Games are still supported by Microsoft and The Scripting Guys, but we’re an independent organization and our actions and words do not represent Microsoft.

Here’s a bit about the tracks…

Beginner track   Consists of events where the answer is usually a one-liner, or at most, a couple of lines of code. We do not usually expect to see error handling or error suppression, extensive use of variables, and so on. We recognize that entries in the Beginner track may sometimes produce errors (like if the command can’t connect to a computer), and that’s fine. Judges will typically be less impressed with overcomplicated solutions, so keep it simple.

Advanced track   Consists of events where the answer is usually an advanced function with parameters. If you don’t know what an advanced function is, the Advanced track is not for you. We expect to see more attention to detail, and more use of built-in Windows PowerShell features.

Caution   We ask that you not include any personally identifiable information in your entries. This includes your name, email address, or other contact information.

There are several ways to win this year:

  • Every time you vote on someone’s entry (giving it a score of 1 to 5, with 1 as “bad” and 5 as “good”—whatever those terms mean to you personally), you earn one pointlet. Each pointlet serves as a prize raffle ticket.
  • You can win by being the crowd favorite! That simply means more people have given you high-scoring votes as part of your Crowd Score. These aren’t professional judges, but their opinion still matters.
  • Our professional judging panel will select their Best and Worst list for each event, and they will blog about what they liked and didn’t like. If you’re in the Best list for one or more judges, your entry will be reviewed by our mighty panel of celebrity judges, who will award First, Second, and Third places.
  • We’ll recognize the winners in each event and track, in addition to the overall winners for each track.

What does it take to impress the public and earn a high crowd score? We have no idea—it’s the public. Be creative and do the right thing.

What does it take to wind up on a judge’s Best list? Have a creative approach to the problem you’re given, and consider some of the guidelines in the next section of this guide.

Important note   “Win” does not mean “prize.” Not every recognized winner will receive a tangible prize (although we’re going to try). Every winner will have the right to use a badge on their PowerShell People profile, and we’ll announce those badges after the Games complete. (Oh, you don’t have a profile? Well, if you want to compete in the Games, there’s no better rehearsal than to write the script needed to set up your Powershell People profile!)

Prizes

We’d like to offer thanks in advance to our presenting sponsors, who are providing the majority of the prizes.

First prizes are awarded by our panel of celebrity judges. These judges will review the events that received the top community vote scores, but will use their own discretion for awarding the prizes. There are no fixed criteria for these prizes.

Note   We’ve got more prizes in the works…stay tuned to the Scripting Games site for news and announcements!

Overall winners across all events

First prize: Complimentary pass (admission only; no expenses are covered) to your choice of Microsoft TechEd North America 2013, TechEd Europe 2013, or TechEd NA 2014.

Second prize: SAPIEN Software Suite 2012 ($699 value) provided by SAPIEN Technologies

Third prize: Five ebooks (average value $200) provided by Manning Press

Event 6

First prize: PrimalScript 2012 ($349 value) provided by SAPIEN Technologies

Third prize: eBook (average value $40) provided by Manning Press

Event 5

First prize: PowerShell Studio 2012 ($349 value) provided by SAPIEN Technologies

Third prize: eBook (average value $40) provided by Manning Press

Event 4

Third prize: eBook (average value $40) provided by Manning Press

Event 3

Third prize: eBook (average value $40) provided by Manning Press

Event 2

Third prize: eBook (average value $40) provided by Manning Press

Event 1

Third prize: eBook (average value $40) provided by Manning Press

Crowd Favorite prizes

These prizes are awarded to the events with the top community vote score. We will award one prize for each event in each track.

Third prize (all events): ebook (average value $40) provided by Manning

Prizes for community voting

The top two community voters will receive a complimentary pass (admission only; no expenses are covered) to the PowerShell Summit North America 2014. “Top voters” will be identified by the quantity of votes (in either track) and by the quality (consistency, fairness) of their votes.

In addition, the following prizes will be raffled, with each vote that is cast acting as a raffle ticket:

  • Four $50.00 gift certificates to the SAPIEN Technologies online store, provided by SAPIEN Technologies
  • Twenty ebooks (average value $40) provided by Manning Press

Why you should vote

If you think you’re not qualified to vote on the entries…well actually, you are qualified. Just ask yourself, “Is this a script or command I’d want running in my production environment? Is this the work of a person I’d hire, if I had the opportunity? Did I learn something from this entry?”

Then vote with your heart. Everyone is qualified. And if you can leave a brief comment about why you voted the way you did, even better. The votes are anonymous, as are entries during the voting period, so be polite and professional, and treat others as you’d want to be treated yourself.

And remember, every vote equates to a prize raffle ticket!

Oh…this should go without saying, but we’re gonna say it anyway: don’t be mean. We do have systems in place to watch for odd voting patterns, like handing out all 1s or all 5s, just to rack up pointlets. We also watch for sequence patterns and other signs of abuse. All of those things trip alarms. We also look into things manually. If we find wrong doing, you’ll be banned from the Games for life. Seriously. Oh, we’ll talk to you about it first, we’re not mean. But we absolutely won’t stand for this system being abused.

The future of the Games is in peer review and voting. Your opinion—the opinion of someone working in a production environment—is what’s important in the real world, not the opinion of some fancy-pants judge.  Part of the Games (the expert judge commentary, for example) will make you a better voter and judge in the future, and that’s how we’re going to help build a better overall Windows PowerShell community. So respect the vote.

We sure hope you’ll play along. Be sure to watch the feed on Scripting Games site for the latest news and announcements!

~Don

Thank you, Don, for this information. Stay tuned. Tomorrow we have the 2013 Scripting Games Competitors Guide.

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

Ed Wilson, Microsoft Scripting Guy 

Strategy for Handling 2013 Scripting Games Events


Summary: Microsoft PowerShell enthusiast, Jeff Wouters, talks about his experience with the 2013 Winter Scripting Games warm-up events.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have a special guest blogger, Microsoft Windows PowerShell enthusiast, Jeff Wouters. Here is a little bit about Jeff…

Photo of Jeff Wouters

Jeff Wouters (B ICT, MCITP, MCSA, MCSE) is a freelance technical consultant from the Netherlands with a main focus on high availability and automation. In Microsoft and Citrix products, he uses technologies such as virtualization, redundancy, clustering, and replication. He also has a great passion for Windows PowerShell and is a founding member of the Dutch PowerShell User Group in 2012.

Jeff has been a speaker at IT events such as E2E Virtualization Conference (formerly known as PubForum), BriForum Chicago, and NGN (Dutch IT community). He speaks and blogs mainly about Windows PowerShell and virtualization, but every now and then something else slips in that piques his interest. Jeff is also a contributing author for a book project where 30+ authors from all over the world are working together to create a Windows PowerShell deep-dive book that will be published in 2013.

Jeff’s contact information:

This year, I’m competing in the Windows PowerShell Scripting Games that will be launched at the Windows PowerShell Summit in April. As a teaser, and to test the new system, it was decided to do a little warm-up event.

Note   The 2013 Winter Scripting Games warm-up events are over. The Scripting Wife wrote about her experience with the warm-up events in The Scripting Wife Talks About the First Warm-Up Event. The announcement for the 2013 Scripting Games (main event) will take place at the Windows PowerShell Summit in April. Stay tuned for more information.

I found that the first exercise of the 2013 Winter Scripting Games warm-ups is something that I was able to use in a few of the Windows PowerShell trainings I’ve been giving. It teaches people to break large scripting projects down into eatable pieces and investigate them. When you do this in a group, you’ll end up having some great discussions about why you are doing it the way you are doing when your colleague is doing it another way. So what I’m providing in this post is my solution, not THE solution.

Last year I participated in the Beginner class. I love challenging myself, so I added a little something to the exercise: To deliver every exercise within one hour after starting to script. I was very happy to actually succeed in that task, although at one event I was cutting it a bit close because I was trying to do something with the wrong cmdlet (I needed to use Get-WinEvent instead of Get-EventLog or Get-Event). But that’s a whole other discussion…

I don’t recommend trying to deliver your scripts within an hour. As I experienced last year, this will greatly diminish your learning experience, which is exactly opposite of the goal for the Scripting Games.

So let’s get back on topic. This year I’ve decided that I want to participate in the Advanced class—mostly because I’ve learned a great deal in the last year and I still want to challenge myself.

I hope that you’ll find this post useful in your coming scripting endeavors.

For the purpose of this post, I’ve split the exercise into separate bullets—we’ll cover them one at a time. I’ve numbered the paragraphs in this post the same as each bullet in the exercise so that you can easily find your way around this rather large post.

  1. You have been asked to create a Windows PowerShell advanced function named Get-DiskSizeInfo.
  2. It must accept one or more computer names as a parameter.
  3. It must use WMI or CIM to query each computer.
  4. For each computer, it must display the percentage of free space, drive letter, total size in gigabytes, and free space in gigabytes.
  5. The script must not display error messages.
  6. If a specified computer cannot be contacted, the function must log the computer name to ‘C:\Errors.txt’.
  7. Optional: Display verbose output showing the name of the computer being contacted.

Note   Although I added a Help function when I wrote the script, I’ve not included it in this post because it would make it even bigger—and it’s big enough as it is, right?

1.    The advanced function

The function has a few requirements. First, it has to be named Get-DiskSizeInfo. Secondly, it needs to be an advanced function.

Many of the students in my Windows PowerShell classes and workshops ask me how they can convert a function into an advanced function. It’s actually pretty easy, just add [CmdletBinding()] to it at the top like so:

function Get-DiskSizeInfo
{
  [CmdletBinding()]
  Param ()
}

See how easy it is? You don’t need to be a rocket scientist to write some Windows PowerShell commands.

2.    A parameter

The second is that the function needs to accept one or more computer names as input via a parameter. You could define a bunch of parameters such as ComputerName1, ComputerName2, ComputerName3, but that’s just plain crazy.

If you were to create a single parameter and make it an array instead of a string, it would fit our needs just fine:

function Get-DiskSizeInfo

{

  [CmdletBinding()]

  Param (

      [Parameter(Mandatory=$false)][array]$ComputerName=$Env:ComputerName

  )

}

Note that I’ve made the parameter NOT mandatory because I’ve given a default value (the local computer name).  If I were to make it mandatory, giving it a default value would be useless because it would prompt me to provide values for the ComputerName parameter (because it’s mandatory).

But this is a rather basic parameter. In fact, I would want to do more with it such as providing it aliases and allowing input from the pipeline. So let’s add some of that:

function Get-DiskSizeInfo

{

  [CmdletBinding()]

  Param (

    [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

    [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

    [array]$ComputerName=$Env:ComputerName

  )

}

As you can see, I also want my function to be able to handle pipeline input. Therefore, I’ll be using a Begin-Process-End construction: 

function Get-DiskSizeInfo

{

  [CmdletBinding(SupportsShouldProcess=$true,PositionalBinding=$false,ConfirmImpact='Low')]

  Param (

      [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

      [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

      [array]$ComputerName=$Env:ComputerName

  )

  Begin { }

  Process { }

  End { }

}

3.    The command

In this case, I’ve chosen to use WMI because not all servers in my production environment support CIM. I could have used the COM protocol combined with the CIM cmdlets, but I have found simply using the WMI cmdlets to be easier. It wasn’t a requirement to NOT use WMI, so I am still working within the boundaries that are set by the exercise.

Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″

But as usual, you’ll get too much information, and you’ll only want the properties that are required. Because there are some additional requirements, such as showing the output value of the total size in GB, I need to do some formatting:

Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ | Select-Object @{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

Note   I’ve seen some people use Label and Expression, whereas others use Name and Expression. Both work just fine, so you can use whatever makes you happy.

Now include this code into the function:

function Get-DiskSizeInfo

{

  [CmdletBinding(SupportsShouldProcess=$true,PositionalBinding=$false,ConfirmImpact='Low')]

  Param ( [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

      [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

      [array]$ComputerName=$Env:ComputerName

  )

  Begin { }

  Process

  {

    Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ | Select-Object @{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

  }

  End { }

}

4.    Display the information for each computer

Displaying the information is actually the easy part. Simply use a ForEach loop, and add a computer name parameter to the Get-WMIObject command: 

function Get-DiskSizeInfo

{ [CmdletBinding(SupportsShouldProcess=$true,PositionalBinding=$false,ConfirmImpact='Low')]

  Param ( [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

      [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

      [array]$ComputerName=$Env:ComputerName

  )

  Begin { }

  Process

  {

    Foreach ($Target in $ComputerName)

    {

      Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ -ComputerName $Target -ErrorVariable $Errors -ErrorAction SilentlyContinue | Select-Object @{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

    }

  }

  End { }

}

5.    No errors displayed

Handling errors can be a little tricky because there are two types of errors: terminating and non-terminating.

Terminating errors will actually terminate your script. So if such an error occurs, it’s the end of the script. If you’re executing the command for multiple objects, you wouldn’t want the script to be terminated half way through, right? So, how can you catch those errors?

Well, that’s it actually…you need to “catch” them with Try-Catch—and to not show them, you need to redirect or pipe them to Null.

In this case, I’ll only be catching the exceptions: 

function Get-DiskSizeInfo

{ [CmdletBinding(SupportsShouldProcess=$true,PositionalBinding=$false,ConfirmImpact='Low')]

  Param ( [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

      [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

      [array]$ComputerName=$Env:ComputerName

  )

  Begin { }

  Process

  {

    Try

    {

      Foreach ($Target in $ComputerName)

      {

        Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ -ComputerName $Target -ErrorVariable $Errors -ErrorAction SilentlyContinue | Select-Object @{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

      }

    }

    catch [System.Exception]

    {

      $Error | Out-Null

    }

    Finally { }

  }

  End { }

}

Note   For more information about how you can use Try/Catch/Finally, take a look at the Hey! Scripting Guy Blog, How Can I Use Try/Catch/Finally in Windows PowerShell? 

The command itself can give errors. For example if a computer can’t be contacted, it will return that the RPC server is unavailable. You can solve this by adding the ErrorAction parameter to the Get-WMIObject cmdlet with a SilentlyContinue value:

function Get-DiskSizeInfo

{ [CmdletBinding(SupportsShouldProcess=$true,PositionalBinding=$false,ConfirmImpact='Low')]

  Param ( [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

      [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

      [array]$ComputerName=$Env:ComputerName

  )

  Begin { }

  Process

  {

    Try

    {

      Foreach ($Target in $ComputerName)

      {

        Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ -ComputerName $Target -ErrorAction SilentlyContinue | Select-Object @{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

      }

    }

    catch [System.Exception]

    {

      $_ | Out-Null

      $Error | Out-Null

    }

    Finally { }

  }

  End { }

}

The reason I’m catching System.Exception here is that this is the base exception class. All other exception classes are derived from this one.

6.    Error logging

No errors are shown, but how do we know if a system could not be contacted? It isn’t shown to the screen—we’ve just made sure that won’t happen. Also, one of the requirements was to write the name of that computer to an error log (C:\Errors.txt) when it can’t be contacted.

First I always like to define the file or even the path of the error log. We can do that at Begin { }:

Begin

{

  $ErrorLogPath = “C:\Errors.txt”

}

If you have Windows PowerShell 3.0, you can use the Out-File cmdlet with the Append parameter. This parameter was introduced in Windows PowerShell 3.0. We need an error written to the log at each loop.

So how do we get those errors? We’ve just made it so that no errors are shown, so where are they?

Windows PowerShell comes with a bunch of error variables. One of those is $?. This variable gives you a $true or $false depending on if the last command completed successfully. So if the variable is false, we know that an error has occurred, right? An error means that the device could not be contacted, no matter what the reason. And we don’t care about the reason because that wasn’t one of the requirements. We only want to log that the device could not be contacted.

function Get-DiskSizeInfo

{ [CmdletBinding(SupportsShouldProcess=$true,PositionalBinding=$false,ConfirmImpact='Low')]

  Param ( [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

      [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

      [array]$ComputerName=$Env:ComputerName

  )

  Begin

  {

    $ErrorLogPath = “C:\Errors.txt”

  }

  Process

  {

    Try

    {

      Foreach ($Target in $ComputerName)

      {

        Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ -ComputerName $Target -ErrorAction SilentlyContinue | Select-Object @{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

        if (!$?) {“Device $Target could not be contacted” | Out-File $ErrorLogPath -Append}

      }

    }

    catch [System.Exception]

    {

      $_ | Out-Null

      $Error | Out-Null

    }

    Finally { }

  }

  End { }

}

7.    Display system name based on the Verbose parameter

This step took some searching because I had not done this before. How do we know if the Verbose parameter has been used?

There probably are some very creative ways of doing this, but do you know that you can use the $PSCmdlet variable? You can use this to check the command you’ve invoked for the presence of a parameter.

So if the parameter is present, we want to do something; and if it’s not, we want to do something else.

function Get-DiskSizeInfo

{ [CmdletBinding(SupportsShouldProcess=$true,PositionalBinding=$false,ConfirmImpact='Low')]

  Param ( [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

      [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

      [array]$ComputerName=$Env:ComputerName

  )

Begin

  {

    $ErrorLogPath = “C:\Errors.txt”

  }

  Process

  {

    Try

    {

      If ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent)

      {

        Foreach ($Target in $ComputerName)

        {

          Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ -ComputerName $Target -ErrorAction SilentlyContinue | Select-Object SystemName,@{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

          if (!$?) {“Device $Target could not be contacted” | Out-File $ErrorLogPath -Append}

        }

      }

      else

      {

        Foreach ($Target in $ComputerName)

        {

          Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ -ComputerName $Target -ErrorAction SilentlyContinue | Select-Object @{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

          if (!$?) {“Device $Target could not be contacted” | Out-File $ErrorLogPath -Append}

        }

      }

    }

    catch [System.Exception]

    {

      $Error | Out-Null

    }

    finally {}

  }

  End

  {

  }

}

My conclusion is also my advice: Break down the exercise into eatable pieces and cover them one at a time. This will make your scripting life and learning experience a whole lot more effective and easier. Trust me on this one. Also take time to properly investigate each part, which will greatly improve your learning experience. You are going to encounter things in your investigation that you didn’t know. But be aware that those investigation will not take you too far away from your goal. Simply do as I do: Make a note of it and look at it sometime in the future…

~Jeff

Jeff, thank you so very much for writing about your experiences in the 2013 Scripting Games warm-up exercises.

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

Ed Wilson, Microsoft Scripting Guy

Strategy for Handling 2013 Scripting Games Events


Summary: Microsoft PowerShell enthusiast, Jeff Wouters, talks about his experience with the 2013 Winter Scripting Games warm-up events.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have a special guest blogger, Microsoft Windows PowerShell enthusiast, Jeff Wouters. Here is a little bit about Jeff…

Photo of Jeff Wouters

Jeff Wouters (B ICT, MCITP, MCSA, MCSE) is a freelance technical consultant from the Netherlands with a main focus on high availability and automation. In Microsoft and Citrix products, he uses technologies such as virtualization, redundancy, clustering, and replication. He also has a great passion for Windows PowerShell and is a founding member of the Dutch PowerShell User Group in 2012.

Jeff has been a speaker at IT events such as E2E Virtualization Conference (formerly known as PubForum), BriForum Chicago, and NGN (Dutch IT community). He speaks and blogs mainly about Windows PowerShell and virtualization, but every now and then something else slips in that piques his interest. Jeff is also a contributing author for a book project where 30+ authors from all over the world are working together to create a Windows PowerShell deep-dive book that will be published in 2013.

Jeff’s contact information:

This year, I’m competing in the Windows PowerShell Scripting Games that will be launched at the Windows PowerShell Summit in April. As a teaser, and to test the new system, it was decided to do a little warm-up event.

Note   The 2013 Winter Scripting Games warm-up events are over. The Scripting Wife wrote about her experience with the warm-up events in The Scripting Wife Talks About the First Warm-Up Event. The announcement for the 2013 Scripting Games (main event) will take place at the Windows PowerShell Summit in April. Stay tuned for more information.

I found that the first exercise of the 2013 Winter Scripting Games warm-ups is something that I was able to use in a few of the Windows PowerShell trainings I’ve been giving. It teaches people to break large scripting projects down into eatable pieces and investigate them. When you do this in a group, you’ll end up having some great discussions about why you are doing it the way you are doing when your colleague is doing it another way. So what I’m providing in this post is my solution, not THE solution.

Last year I participated in the Beginner class. I love challenging myself, so I added a little something to the exercise: To deliver every exercise within one hour after starting to script. I was very happy to actually succeed in that task, although at one event I was cutting it a bit close because I was trying to do something with the wrong cmdlet (I needed to use Get-WinEvent instead of Get-EventLog or Get-Event). But that’s a whole other discussion…

I don’t recommend trying to deliver your scripts within an hour. As I experienced last year, this will greatly diminish your learning experience, which is exactly opposite of the goal for the Scripting Games.

So let’s get back on topic. This year I’ve decided that I want to participate in the Advanced class—mostly because I’ve learned a great deal in the last year and I still want to challenge myself.

I hope that you’ll find this post useful in your coming scripting endeavors.

For the purpose of this post, I’ve split the exercise into separate bullets—we’ll cover them one at a time. I’ve numbered the paragraphs in this post the same as each bullet in the exercise so that you can easily find your way around this rather large post.

  1. You have been asked to create a Windows PowerShell advanced function named Get-DiskSizeInfo.
  2. It must accept one or more computer names as a parameter.
  3. It must use WMI or CIM to query each computer.
  4. For each computer, it must display the percentage of free space, drive letter, total size in gigabytes, and free space in gigabytes.
  5. The script must not display error messages.
  6. If a specified computer cannot be contacted, the function must log the computer name to ‘C:\Errors.txt’.
  7. Optional: Display verbose output showing the name of the computer being contacted.

Note   Although I added a Help function when I wrote the script, I’ve not included it in this post because it would make it even bigger—and it’s big enough as it is, right?

1.    The advanced function

The function has a few requirements. First, it has to be named Get-DiskSizeInfo. Secondly, it needs to be an advanced function.

Many of the students in my Windows PowerShell classes and workshops ask me how they can convert a function into an advanced function. It’s actually pretty easy, just add [CmdletBinding()] to it at the top like so:

function Get-DiskSizeInfo
{
  [CmdletBinding()]
  Param ()
}

See how easy it is? You don’t need to be a rocket scientist to write some Windows PowerShell commands.

2.    A parameter

The second is that the function needs to accept one or more computer names as input via a parameter. You could define a bunch of parameters such as ComputerName1, ComputerName2, ComputerName3, but that’s just plain crazy.

If you were to create a single parameter and make it an array instead of a string, it would fit our needs just fine:

function Get-DiskSizeInfo

{

  [CmdletBinding()]

  Param (

      [Parameter(Mandatory=$false)][array]$ComputerName=$Env:ComputerName

  )

}

Note that I’ve made the parameter NOT mandatory because I’ve given a default value (the local computer name).  If I were to make it mandatory, giving it a default value would be useless because it would prompt me to provide values for the ComputerName parameter (because it’s mandatory).

But this is a rather basic parameter. In fact, I would want to do more with it such as providing it aliases and allowing input from the pipeline. So let’s add some of that:

function Get-DiskSizeInfo

{

  [CmdletBinding()]

  Param (

    [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

    [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

    [array]$ComputerName=$Env:ComputerName

  )

}

As you can see, I also want my function to be able to handle pipeline input. Therefore, I’ll be using a Begin-Process-End construction: 

function Get-DiskSizeInfo

{

  [CmdletBinding(SupportsShouldProcess=$true,PositionalBinding=$false,ConfirmImpact='Low')]

  Param (

      [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

      [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

      [array]$ComputerName=$Env:ComputerName

  )

  Begin { }

  Process { }

  End { }

}

3.    The command

In this case, I’ve chosen to use WMI because not all servers in my production environment support CIM. I could have used the COM protocol combined with the CIM cmdlets, but I have found simply using the WMI cmdlets to be easier. It wasn’t a requirement to NOT use WMI, so I am still working within the boundaries that are set by the exercise.

Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″

But as usual, you’ll get too much information, and you’ll only want the properties that are required. Because there are some additional requirements, such as showing the output value of the total size in GB, I need to do some formatting:

Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ | Select-Object @{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

Note   I’ve seen some people use Label and Expression, whereas others use Name and Expression. Both work just fine, so you can use whatever makes you happy.

Now include this code into the function:

function Get-DiskSizeInfo

{

  [CmdletBinding(SupportsShouldProcess=$true,PositionalBinding=$false,ConfirmImpact='Low')]

  Param ( [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

      [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

      [array]$ComputerName=$Env:ComputerName

  )

  Begin { }

  Process

  {

    Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ | Select-Object @{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

  }

  End { }

}

4.    Display the information for each computer

Displaying the information is actually the easy part. Simply use a ForEach loop, and add a computer name parameter to the Get-WMIObject command: 

function Get-DiskSizeInfo

{ [CmdletBinding(SupportsShouldProcess=$true,PositionalBinding=$false,ConfirmImpact='Low')]

  Param ( [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

      [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

      [array]$ComputerName=$Env:ComputerName

  )

  Begin { }

  Process

  {

    Foreach ($Target in $ComputerName)

    {

      Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ -ComputerName $Target -ErrorVariable $Errors -ErrorAction SilentlyContinue | Select-Object @{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

    }

  }

  End { }

}

5.    No errors displayed

Handling errors can be a little tricky because there are two types of errors: terminating and non-terminating.

Terminating errors will actually terminate your script. So if such an error occurs, it’s the end of the script. If you’re executing the command for multiple objects, you wouldn’t want the script to be terminated half way through, right? So, how can you catch those errors?

Well, that’s it actually…you need to “catch” them with Try-Catch—and to not show them, you need to redirect or pipe them to Null.

In this case, I’ll only be catching the exceptions: 

function Get-DiskSizeInfo

{ [CmdletBinding(SupportsShouldProcess=$true,PositionalBinding=$false,ConfirmImpact='Low')]

  Param ( [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

      [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

      [array]$ComputerName=$Env:ComputerName

  )

  Begin { }

  Process

  {

    Try

    {

      Foreach ($Target in $ComputerName)

      {

        Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ -ComputerName $Target -ErrorVariable $Errors -ErrorAction SilentlyContinue | Select-Object @{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

      }

    }

    catch [System.Exception]

    {

      $Error | Out-Null

    }

    Finally { }

  }

  End { }

}

Note   For more information about how you can use Try/Catch/Finally, take a look at the Hey! Scripting Guy Blog, How Can I Use Try/Catch/Finally in Windows PowerShell? 

The command itself can give errors. For example if a computer can’t be contacted, it will return that the RPC server is unavailable. You can solve this by adding the ErrorAction parameter to the Get-WMIObject cmdlet with a SilentlyContinue value:

function Get-DiskSizeInfo

{ [CmdletBinding(SupportsShouldProcess=$true,PositionalBinding=$false,ConfirmImpact='Low')]

  Param ( [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

      [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

      [array]$ComputerName=$Env:ComputerName

  )

  Begin { }

  Process

  {

    Try

    {

      Foreach ($Target in $ComputerName)

      {

        Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ -ComputerName $Target -ErrorAction SilentlyContinue | Select-Object @{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

      }

    }

    catch [System.Exception]

    {

      $_ | Out-Null

      $Error | Out-Null

    }

    Finally { }

  }

  End { }

}

The reason I’m catching System.Exception here is that this is the base exception class. All other exception classes are derived from this one.

6.    Error logging

No errors are shown, but how do we know if a system could not be contacted? It isn’t shown to the screen—we’ve just made sure that won’t happen. Also, one of the requirements was to write the name of that computer to an error log (C:\Errors.txt) when it can’t be contacted.

First I always like to define the file or even the path of the error log. We can do that at Begin { }:

Begin

{

  $ErrorLogPath = “C:\Errors.txt”

}

If you have Windows PowerShell 3.0, you can use the Out-File cmdlet with the Append parameter. This parameter was introduced in Windows PowerShell 3.0. We need an error written to the log at each loop.

So how do we get those errors? We’ve just made it so that no errors are shown, so where are they?

Windows PowerShell comes with a bunch of error variables. One of those is $?. This variable gives you a $true or $false depending on if the last command completed successfully. So if the variable is false, we know that an error has occurred, right? An error means that the device could not be contacted, no matter what the reason. And we don’t care about the reason because that wasn’t one of the requirements. We only want to log that the device could not be contacted.

function Get-DiskSizeInfo

{ [CmdletBinding(SupportsShouldProcess=$true,PositionalBinding=$false,ConfirmImpact='Low')]

  Param ( [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

      [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

      [array]$ComputerName=$Env:ComputerName

  )

  Begin

  {

    $ErrorLogPath = “C:\Errors.txt”

  }

  Process

  {

    Try

    {

      Foreach ($Target in $ComputerName)

      {

        Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ -ComputerName $Target -ErrorAction SilentlyContinue | Select-Object @{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

        if (!$?) {“Device $Target could not be contacted” | Out-File $ErrorLogPath -Append}

      }

    }

    catch [System.Exception]

    {

      $_ | Out-Null

      $Error | Out-Null

    }

    Finally { }

  }

  End { }

}

7.    Display system name based on the Verbose parameter

This step took some searching because I had not done this before. How do we know if the Verbose parameter has been used?

There probably are some very creative ways of doing this, but do you know that you can use the $PSCmdlet variable? You can use this to check the command you’ve invoked for the presence of a parameter.

So if the parameter is present, we want to do something; and if it’s not, we want to do something else.

function Get-DiskSizeInfo

{ [CmdletBinding(SupportsShouldProcess=$true,PositionalBinding=$false,ConfirmImpact='Low')]

  Param ( [Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ValueFromRemainingArguments=$false,Position=0)]

      [ValidateNotNull()][ValidateNotNullOrEmpty()][Alias("Name","Computer")]

      [array]$ComputerName=$Env:ComputerName

  )

Begin

  {

    $ErrorLogPath = “C:\Errors.txt”

  }

  Process

  {

    Try

    {

      If ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent)

      {

        Foreach ($Target in $ComputerName)

        {

          Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ -ComputerName $Target -ErrorAction SilentlyContinue | Select-Object SystemName,@{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

          if (!$?) {“Device $Target could not be contacted” | Out-File $ErrorLogPath -Append}

        }

      }

      else

      {

        Foreach ($Target in $ComputerName)

        {

          Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType=3″ -ComputerName $Target -ErrorAction SilentlyContinue | Select-Object @{Label=”Drive”;Expression={$_.DeviceID}},@{Label=”FreeSpace(GB)”;Expression={“{0:N1}” -f($_.FreeSpace/1GB)}},@{Label=”Size(GB)”;Expression={“{0:N1}” -f($_.Size/1GB)}},@{Label=”PercentFree”;Expression={“{0:N0}” -f(($_.freespace * 100) / $_.Size)}}

          if (!$?) {“Device $Target could not be contacted” | Out-File $ErrorLogPath -Append}

        }

      }

    }

    catch [System.Exception]

    {

      $Error | Out-Null

    }

    finally {}

  }

  End

  {

  }

}

My conclusion is also my advice: Break down the exercise into eatable pieces and cover them one at a time. This will make your scripting life and learning experience a whole lot more effective and easier. Trust me on this one. Also take time to properly investigate each part, which will greatly improve your learning experience. You are going to encounter things in your investigation that you didn’t know. But be aware that those investigation will not take you too far away from your goal. Simply do as I do: Make a note of it and look at it sometime in the future…

~Jeff

Jeff, thank you so very much for writing about your experiences in the 2013 Scripting Games warm-up exercises.

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

Ed Wilson, Microsoft Scripting Guy