<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://powershell.com/cs/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Search results for 'app:weblogs' matching tags 'scripting techniques', 'guest blogger', and 'regular expressions'</title><link>http://powershell.com/cs/search/SearchResults.aspx?q=app:weblogs&amp;tag=scripting+techniques,guest+blogger,regular+expressions&amp;orTags=0&amp;o=DateDescending</link><description>Search results for 'app:weblogs' matching tags 'scripting techniques', 'guest blogger', and 'regular expressions'</description><dc:language>en-US</dc:language><generator>CommunityServer 2008.5 (Build: 30929.2835)</generator><item><title>Use PowerShell to Move Computers Based on IP Addresses: Part 2</title><link>http://powershell.com/cs/blogs/hey-scriptingguy/archive/2011/12/04/use-powershell-to-move-computers-based-on-ip-addresses-part-2.aspx</link><pubDate>Sun, 04 Dec 2011 06:00:00 GMT</pubDate><guid isPermaLink="false">f421715f-7aba-45f0-8a8d-44de5318a3a7:13464</guid><dc:creator>Anonymous</dc:creator><description>&lt;p&gt;&lt;b&gt;Summary&lt;/b&gt;: In this blog, Eric Wright revises his script by using Active Directory cmdlets to move computers that are organized in Active Directory, based on their IP addresses.&lt;/p&gt;
&lt;p&gt;Microsoft Scripting Guy, Ed Wilson, is here. In today&amp;rsquo;s post, guest blogger, Eric Wright, reprises &lt;a href="http://blogs.technet.com/b/heyscriptingguy/archive/2011/12/03/use-powershell-to-move-computers-based-on-ip-addresses.aspx" target="_blank"&gt;yesterday&amp;rsquo;s post&lt;/a&gt; to use the Windows Active Directory module cmdlets. Here is a bit about Eric.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/2018.wes_2D00_12_2D00_4_2D00_11_2D00_1.jpg"&gt;&lt;img src="http://blogs.technet.com/resized-image.ashx/__size/250x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/2018.wes_2D00_12_2D00_4_2D00_11_2D00_1.jpg" alt="Photo of Eric Wright" title="Photo of Eric Wright" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;I am a systems architect and blogger, and I work with Microsoft tools, Windows PowerShell, virtualization, and various web technologies. I&amp;rsquo;m a big fan of automation and scripting to simplify and enhance systems administration.&lt;br /&gt; Contact information:&lt;br /&gt; Website: &lt;a href="http://www.discoposse.com/" target="_blank"&gt;DiscoPosse&amp;mdash;Using the chicken to measure IT&lt;/a&gt;&lt;br /&gt; Twitter: &lt;a href="http://www.twitter.com/discoposse" target="_blank"&gt;http://www.twitter.com/discoposse&lt;/a&gt; &lt;br /&gt; LinkedIn: &lt;a href="http://ca.linkedin.com/pub/eric-wright/3/7b4/bb6" target="_blank"&gt;http://ca.linkedin.com/pub/eric-wright/3/7b4/bb6&lt;/a&gt;&lt;span style="text-decoration:underline;"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Note&lt;/b&gt;: This script requires Active Directory domain controllers with Windows Server&amp;nbsp;2008&amp;nbsp;R2 or the Active Directory Management Gateway Service. For more information about installing the gateway service, see the Hey, Scripting Guy! Blog &lt;a href="http://blogs.technet.com/b/heyscriptingguy/archive/2011/08/30/install-active-directory-management-service-for-easy-powershell-access.aspx" target="_blank"&gt;Install Active Directory Management Service for Easy PowerShell Access&lt;/a&gt;. With Windows Server&amp;nbsp;2008&amp;nbsp;R2, you can use the native Active Directory PowerShell module. If you are running an earlier version of Windows Server on your domain controllers, or if you do not have the Active Directory Management Gateway Service installed, you can use the process that &lt;a href="http://blogs.technet.com/b/heyscriptingguy/archive/2011/12/03/use-powershell-to-move-computers-based-on-ip-addresses.aspx" target="_blank"&gt;I documented yesterday in part 1&lt;/a&gt;, which uses the Windows PowerShell snap-in, &lt;a href="http://www.quest.com/powershell/activeroles-server.aspx" target="_blank"&gt;Quest ActiveRoles&lt;/a&gt;.&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;In my organization, I have chosen to organize my Active Directory (AD) organizational unit (OU) structure based on physical locations. A common challenge is that our technical support team does not always move computer accounts into the proper structure in Active Directory. Another issue is that computers may not be deleted from the domain when they are decommissioned. This confuses other processes that use Active Directory as their authoritative source for computer object information.&lt;/p&gt;
&lt;p&gt;To tackle this issue, I created a Windows PowerShell script that runs as a batch process and will move the computer objects into OUs based on their IP addresses.&lt;/p&gt;
&lt;p&gt;In my example, I am looking for only Windows&amp;nbsp;7 computers, but this can be flavored to match any selection criteria you need. The structure of the script is to do the following:&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Check the operating system for Windows 7 (any version).&lt;/li&gt;
&lt;li&gt;Check to see if the computer has been off the domain.&lt;/li&gt;
&lt;li&gt;If the computer has been off the network for 60 days, move it to a &amp;ldquo;Disabled&amp;rdquo; OU.&lt;/li&gt;
&lt;li&gt;If the computer has been off the network for 90 days, delete it.&lt;/li&gt;
&lt;li&gt;Check for the last DNS registration of the computer, and move it to an OU based on its IP information.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We also need to define the IP subnets and the OU structure so that we can match the computer object&amp;rsquo;s IP information and move it to its correct location in AD.&lt;/p&gt;
&lt;p&gt;First, we load the ActiveDirectory module as follows:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Import-Module ActiveDirectory&lt;/p&gt;
&lt;p&gt;Next, we want to define two parameters for the age of the computers. I call these &lt;b&gt;$old&lt;/b&gt; and &lt;b&gt;$veryold&lt;/b&gt;, and for my example, I have set them as 60 days and 90 days respectively. You can adjust these easily to suit your needs.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$old = (Get-Date).AddDays(-60) # Modify the -60 to match your threshold&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$veryold = (Get-Date).AddDays(-90) # Modify the -90 to match your threshold&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Now the fun part! Because we will capture the IP information as a string and not an integer, this makes it a bit more challenging to figure out what subnet we are in. This example has three subnets, which are 192.168.1.0/24, 192.168.2.0/24 and 192.168.3.0/24. I have chosen class C subnets for this script to match my structure, but you may have to get more creative if you have a more complex network configuration.&lt;/p&gt;
&lt;p&gt;We will define our IP range variables as Regular Expressions (or Regex as they are commonly known) so that we can match the characters appropriately. Sorry kids, but it is goodbye GUI and hello Regex for this stuff.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Site1IPRange = &amp;quot;\b(?:(?:192)\.)&amp;quot; + &amp;quot;\b(?:(?:168)\.)&amp;quot; + &amp;quot;\b(?:(?:1)\.)&amp;quot; + &amp;quot;\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))&amp;quot; # 192.168.1.0/24&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Site2IPRange = &amp;quot;\b(?:(?:192)\.)&amp;quot; + &amp;quot;\b(?:(?:168)\.)&amp;quot; + &amp;quot;\b(?:(?:2)\.)&amp;quot; + &amp;quot;\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))&amp;quot; # 192.168.2.0/24&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Site3IPRange = &amp;quot;\b(?:(?:192)\.)&amp;quot; + &amp;quot;\b(?:(?:168)\.)&amp;quot; + &amp;quot;\b(?:(?:3)\.)&amp;quot; + &amp;quot;\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))&amp;quot; # 192.168.3.0/24&lt;/p&gt;
&lt;p&gt;I know you are probably thinking it is time to just retrain the support staff to do this, right? Do not be frightened away just yet. Regex is easier than you may think once you use it more and can break it down into sensible chunks. It is as simple as reading a map (OK, that is not always simple).&lt;/p&gt;
&lt;p align="center"&gt;Here Be Regex Dragons!&lt;/p&gt;
&lt;p align="center"&gt;&amp;nbsp;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/5707.wes_2D00_12_2D00_4_2D00_11_2D00_2.jpg"&gt;&lt;img src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/5707.wes_2D00_12_2D00_4_2D00_11_2D00_2.jpg" alt="Image of map" title="Image of map" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The key information we see is pretty readable. Because we know the first three octets are static we define them easily, as follows:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;ldquo;\b(?:(?:192)\.)&amp;rdquo; + &amp;ldquo;\b(?:(?:168)\.)&amp;rdquo; + &amp;ldquo;\b(?:(?:1)\.)&amp;rdquo;&lt;/p&gt;
&lt;p&gt;This shows us matching as 192.168.1., which takes care of the first three octets. Because it is a class C IP range, we want to capture from 0-255 in the fourth octet, which is done like this:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;ldquo;\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))&amp;rdquo;&lt;/p&gt;
&lt;p&gt;We read the string and look for matching of three distinct ranges, which are 0-199, 200-249, or 250-255. The &lt;b&gt;OR &lt;/b&gt;is the important part of the phrasing, and it is represented by the &lt;b&gt;|&lt;/b&gt; symbol, which is known as the &amp;ldquo;pipe&amp;rdquo; symbol to most.&lt;/p&gt;
&lt;p&gt;Here is the breakdown of those three ranges:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&lt;b&gt;250-255&lt;/b&gt;: 25[0-5]&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&lt;b&gt;200-249&lt;/b&gt;: 2[0-4][0-9]&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&lt;b&gt;0-199&lt;/b&gt;: [01]?[0-9][0-9]?&lt;/p&gt;
&lt;p&gt;OK, we are through the tough part. Now we define the OU structure for the Disabled and the three locations:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$DisabledDN = &amp;quot;OU=Disabled,DC=yourdomain,DC=com&amp;quot;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Site1DN = &amp;quot;OU=Site1,DC=yourdomain,DC=com&amp;quot;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Site2DN = &amp;quot;OU=Site2,DC=yourdomain,DC=com&amp;quot;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Site3DN = &amp;quot;OU=Site3,DC=yourdomain,DC=com&amp;quot;&lt;/p&gt;
&lt;p&gt;This is where we begin the object query. Because we want to find out how &amp;ldquo;old&amp;rdquo; the computer account is, we will bring in the &lt;b&gt;PasswordLastSet&lt;/b&gt; property from Active Directory, in addition to the default values. (Note that the native Microsoft parameter uses &lt;b&gt;PasswordLastSet&lt;/b&gt;, whereas the Quest ActiveRoles parameter in yesterday&amp;#39;s blog uses&lt;b&gt; pwdLastSet&lt;/b&gt;.) This will tell us the last time the hidden password that is negotiated between the computer account and Active Directory has been reset. The default maximum duration is 30 days, so as long as a computer is connecting to the domain regularly it, should always be less than 30 days old.&lt;/p&gt;
&lt;p&gt;If you want to modify the operating systems that get captured, you simply change the selection parameters of the &lt;b&gt;Get-ADComputer&lt;/b&gt; query as shown here:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Get-ADComputer -Filter { OperatingSystem -like &amp;quot;Windows 7*&amp;quot; } -Properties PasswordLastSet | ForEach-Object {THE REST OF OUR SCRIPT GOES IN HERE }&lt;/p&gt;
&lt;p&gt;Our script will query AD for each &lt;b&gt;Computer&lt;/b&gt; object, and we will run the next bunch of processes against each object in the &lt;b&gt;ForEach-Object&lt;/b&gt; loop. All of the following content is stored inside the curly brackets.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s ignore any failure messages from the IP lookup with this:&lt;/p&gt;
&lt;p&gt;trap [System.Net.Sockets.SocketException] { continue; }&lt;/p&gt;
&lt;p&gt;We need to use the Computer name, DN, and &lt;b&gt;PasswordLastSet&lt;/b&gt; so let&amp;rsquo;s set those as variables from the query result. We also want to capture the current container, so we use a simple &lt;b&gt;Replace&lt;/b&gt; command to derive the current OU location:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ComputerName = $_.Name&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ComputerDN = $_.DN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ComputerPasswordLastSet = $_.PasswordLastSet&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ComputerContainer = $ComputerDN.Replace( &amp;quot;CN=$ComputerName,&amp;quot; , &amp;quot;&amp;quot;)&lt;/p&gt;
&lt;p&gt;Now we can work with the Computer account age and delete or move them as necessary:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;# If the computer is more than 90 days off the network, remove the computer object&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;if ($ComputerPasswordLastSet -le $veryold) {&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Remove-ADObject -Identity $ComputerDN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;# Check to see if it is an &amp;quot;old&amp;quot; computer account and move it to the Disabled\Computers OU&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;if ($ComputerPasswordLastSet -le $old) {&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $DestinationDN = $DisabledDN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Move-ADObject -Identity $ComputerDN -TargetPath $DestinationDN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Next, we query DNS for the IP address of the computer. We will set the &lt;b&gt;$IP&lt;/b&gt; value as $NULL first, so that if the query fails it will be dealt with correctly later in the process. If we don&amp;rsquo;t set the NULL value, it retains the IP from the last lookup, and it will move the computer incorrectly.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$IP = $NULL&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$IP = [System.Net.Dns]::GetHostAddresses(&amp;quot;$ComputerName&amp;quot;)&lt;/p&gt;
&lt;p&gt;Now it is time to check for the IP range to set the destination DN accordingly. If you have a majority of systems in some network ranges, you may want to move those up to the top of the &lt;b&gt;If&lt;/b&gt; statement so that they are processed early, which will save some time:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;if ($IP -match $Site1IPRange) {&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $DestinationDN = $Site1DN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;ElseIf ($IP -match $Site2IPRange) {&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $DestinationDN = $Site2DN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;ElseIf ($IP -match $Site3IPRange) {&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $DestinationDN = $Site3DN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Else {&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # If the subnet does not match we should not move the computer so we do Nothing&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $DestinationDN = $ComputerContainer&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p&gt;And here is the last step to actually move the object to the new destination OU. This is where our NULL IP comes into play because we have assumed that if the IP is NULL, it is &amp;ldquo;off network&amp;rdquo; and the aged account process has already dealt with it:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;if ($IP -ne $NULL) {&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Move-ADObject -Identity $ComputerDN -TargetPath $DestinationDN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p&gt;And we made it! Another exciting tip with this script is you can run all of the &lt;b&gt;ADObject&lt;/b&gt; cmdlets with the &lt;i&gt;WhatIf&lt;/i&gt; parameter, which will output the result to the screen rather than perform the move or delete, so you can test drive the script before you implement it.&lt;/p&gt;
&lt;p&gt;Here is this &lt;a href="http://gallery.technet.microsoft.com/PowerShell-Organize-d37c2a29" target="_blank"&gt;script in its full form&lt;/a&gt; from the TechNet Resources Gallery.&lt;/p&gt;
&lt;p&gt;Thank you, Eric. This has been a great series. I appreciate you taking the time to share your knowledge with us.&lt;/p&gt;
&lt;p&gt;I invite you to follow me on &lt;a href="http://bit.ly/scriptingguystwitter" target="_blank"&gt;Twitter&lt;/a&gt; and &lt;a href="http://bit.ly/scriptingguysfacebook" target="_blank"&gt;Facebook&lt;/a&gt;. If you have any questions, send email to me at &lt;a href="mailto:scripter@microsoft.com" target="_blank"&gt;scripter@microsoft.com&lt;/a&gt;, or post your questions on the &lt;a href="http://bit.ly/scriptingforum" target="_blank"&gt;Official Scripting Guys Forum&lt;/a&gt;. See you tomorrow. Until then, peace.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Ed Wilson, Microsoft Scripting Guy&lt;/b&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.technet.com/aggbug.aspx?PostID=3468377" width="1" height="1" alt="" /&gt;</description></item><item><title>Use PowerShell to Move Computers Based on IP Addresses: Part 1</title><link>http://powershell.com/cs/blogs/hey-scriptingguy/archive/2011/12/03/use-powershell-to-move-computers-based-on-ip-addresses-part-1.aspx</link><pubDate>Sat, 03 Dec 2011 06:00:00 GMT</pubDate><guid isPermaLink="false">f421715f-7aba-45f0-8a8d-44de5318a3a7:13460</guid><dc:creator>Anonymous</dc:creator><description>&lt;p&gt;&lt;b&gt;Summary&lt;/b&gt;: In this guest blog post written by Eric Wright, you will learn how to use the Windows PowerShell snap-in, Quest ActiveRoles, to move computers that are organized in Active Directory, based on their IP addresses.&lt;/p&gt;
&lt;p&gt;Microsoft Scripting Guy, Ed Wilson, is here. Guest Blogger Weekend continues. Today we have a guest blog written by Eric Wright. Here is what Eric has to say about himself.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/2604.wes_2D00_12_2D00_3_2D00_11_2D00_1.jpg"&gt;&lt;img src="http://blogs.technet.com/resized-image.ashx/__size/250x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/2604.wes_2D00_12_2D00_3_2D00_11_2D00_1.jpg" alt="Photo of Eric Wright" title="Photo of Eric Wright" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;I am a systems architect and blogger, and I work with Microsoft tools, Windows PowerShell, virtualization, and various web technologies. I&amp;rsquo;m a big fan of automation and scripting to simplify and enhance systems administration.&lt;br /&gt; Contact information:&lt;br /&gt; Website: &lt;a href="http://www.discoposse.com/" target="_blank"&gt;DiscoPosse&amp;mdash;Using the chicken to measure IT&lt;/a&gt;&lt;br /&gt; Twitter: &lt;a href="http://www.twitter.com/discoposse" target="_blank"&gt;http://www.twitter.com/discoposse&lt;/a&gt; &lt;br /&gt; LinkedIn: &lt;a href="http://ca.linkedin.com/pub/eric-wright/3/7b4/bb6" target="_blank"&gt;http://ca.linkedin.com/pub/eric-wright/3/7b4/bb6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Note&lt;/b&gt;: This script uses the Windows PowerShell snap-in, &lt;a href="http://www.quest.com/powershell/activeroles-server.aspx" target="_blank"&gt;Quest ActiveRoles&lt;/a&gt;. This process will work with any version of Windows Server, but you must use Quest ActiveRoles if you are running a version earlier than Windows Server&amp;nbsp;2008&amp;nbsp;R2 on your domain controllers. &lt;a href="http://blogs.technet.com/b/heyscriptingguy/archive/2011/12/04/use-powershell-to-move-computers-based-on-ip-addresses-part-2.aspx" target="_blank"&gt;Tomorrow in Part 2&lt;/a&gt; of this blog, I document the exact process, but we use Active Directory domain controllers with Windows Server&amp;nbsp;2008&amp;nbsp;R2 or the Active Directory Management Gateway Service.&lt;/p&gt;
&lt;p&gt;In my organization, I have chosen to organize my Active Directory (AD) organizational unit (OU) structure based on physical locations. A common challenge is that our technical support team does not always move computer accounts into the proper structure in Active Directory. Another issue is that computers may not be deleted from the domain when they are decommissioned. This confuses other processes that use Active Directory as their authoritative source for computer object information.&lt;/p&gt;
&lt;p&gt;To tackle this issue, I created a Windows PowerShell script that runs as a batch process and will move the computer objects into OUs based on their IP addresses.&lt;/p&gt;
&lt;p&gt;In my example, I am looking for only Windows&amp;nbsp;7 computers, but this can be flavored to match any selection criteria you need. The structure of the script is to do the following:&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Check the operating system for Windows 7 (any version).&lt;/li&gt;
&lt;li&gt;Check to see if the computer has been off the domain.&lt;/li&gt;
&lt;li&gt;If the computer has been off the network for 60 days, move it to a &amp;ldquo;Disabled&amp;rdquo; OU.&lt;/li&gt;
&lt;li&gt;If the computer has been off the network for 90 days, delete it.&lt;/li&gt;
&lt;li&gt;Check for the last DNS registration of the computer, and move it to an OU based on its IP information.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For our script to work, we need to have the Windows PowerShell snap-in, &lt;a href="http://www.quest.com/powershell/activeroles-server.aspx" target="_blank"&gt;Quest ActiveRoles&lt;/a&gt;, installed on the computer that will be running the script for us.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;If you are running Active Directory Domain Controllers with Windows Server&amp;nbsp;2008&amp;nbsp;R2, you can use the native ActiveDirectory Windows PowerShell module. I will post the script for using the Windows Server&amp;nbsp;2008&amp;nbsp;R2 module tomorrow in part 2 of this series.&lt;/p&gt;
&lt;p&gt;We will also need to define the IP subnets and the OU structure so that we can match the computer object&amp;rsquo;s IP information and move it to its correct location in AD.&lt;/p&gt;
&lt;p&gt;First we load the Quest ActiveRoles snap-in as shown here:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Add-PsSnapIn Quest.ActiveRoles.ADManagement -ErrorAction SilentlyContinue&lt;/p&gt;
&lt;p&gt;Next, we want to define two parameters for the age of the computers. I call these &lt;b&gt;$old&lt;/b&gt; and &lt;b&gt;$veryold&lt;/b&gt;, and for my example, I have set them as 60 days and 90 days respectively. You can adjust these easily to suit your needs.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$old = (Get-Date).AddDays(-60) # Modify the -60 to match your threshold&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$veryold = (Get-Date).AddDays(-90) # Modify the -90 to match your threshold&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Now the fun part! Because will capture the IP information as a string and not as an integer, this makes it a bit more challenging to figure out what subnet we are in. This example has three subnets: 192.168.1.0/24, 192.168.2.0/24, and 192.168.3.0/24. I have chosen class C subnets for this script to match my structure, but you may have to get more creative if you have a more complex network configuration.&lt;/p&gt;
&lt;p&gt;We will define our IP range variables as regular expressions (or Regex as they are commonly known), so that we can match the characters appropriately. Sorry kids, but it is goodbye GUI and hello Regex for this stuff.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Site1IPRange = &amp;quot;\b(?:(?:192)\.)&amp;quot; + &amp;quot;\b(?:(?:168)\.)&amp;quot; + &amp;quot;\b(?:(?:1)\.)&amp;quot; + &amp;quot;\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))&amp;quot; # 192.168.1.0/24&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Site2IPRange = &amp;quot;\b(?:(?:192)\.)&amp;quot; + &amp;quot;\b(?:(?:168)\.)&amp;quot; + &amp;quot;\b(?:(?:2)\.)&amp;quot; + &amp;quot;\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))&amp;quot; # 192.168.2.0/24&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Site3IPRange = &amp;quot;\b(?:(?:192)\.)&amp;quot; + &amp;quot;\b(?:(?:168)\.)&amp;quot; + &amp;quot;\b(?:(?:3)\.)&amp;quot; + &amp;quot;\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))&amp;quot; # 192.168.3.0/24&lt;/p&gt;
&lt;p&gt;I know you are probably thinking it is time to just retrain the support staff to do this, right? Do not be frightened away just yet. Regex is easier than you may think once you use it more and can break it down into sensible chunks. It is as simple as reading a map (OK, that is not always simple).&lt;/p&gt;
&lt;p align="center"&gt;Here Be Regex Dragons!&lt;/p&gt;
&lt;p align="center"&gt;&amp;nbsp;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/4452.wes_2D00_12_2D00_3_2D00_11_2D00_2.jpg"&gt;&lt;img src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/4452.wes_2D00_12_2D00_3_2D00_11_2D00_2.jpg" alt="Image of map" title="Image of map" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The key information we see here is pretty readable. Because we know the first three octets are static, we define them easily, as follows:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;ldquo;\b(?:(?:192)\.)&amp;rdquo; + &amp;ldquo;\b(?:(?:168)\.)&amp;rdquo; + &amp;ldquo;\b(?:(?:1)\.)&amp;rdquo;&lt;/p&gt;
&lt;p&gt;This shows us matching as 192.168.1., which takes care of the first three octets. Because it is a class C IP range, we want to capture from 0-255 in the fourth octet, which is done like this:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;ldquo;\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))&amp;rdquo;&lt;/p&gt;
&lt;p&gt;We read the string and look for matching of three distinct ranges, which are 0-199, 200-249, or 250-255. The &lt;b&gt;OR &lt;/b&gt;is the important part of the phrasing, and it is represented by the &lt;b&gt;|&lt;/b&gt; symbol, which is known as the &amp;ldquo;pipe&amp;rdquo; symbol to most.&lt;/p&gt;
&lt;p&gt;Here is the breakdown of those three ranges:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&lt;b&gt;250-255&lt;/b&gt;: 25[0-5]&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&lt;b&gt;200-249&lt;/b&gt;: 2[0-4][0-9]&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&lt;b&gt;0-199&lt;/b&gt;: [01]?[0-9][0-9]?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;OK, we are through the tough part. Now we define the OU structure for the Disabled and the three locations:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$DisabledDN = &amp;quot;OU=Disabled,DC=yourdomain,DC=com&amp;quot;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Site1DN = &amp;quot;OU=Site1,DC=yourdomain,DC=com&amp;quot;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Site2DN = &amp;quot;OU=Site2,DC=yourdomain,DC=com&amp;quot;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$Site3DN = &amp;quot;OU=Site3,DC=yourdomain,DC=com&amp;quot;&lt;/p&gt;
&lt;p&gt;This is where we begin the object query. Because we want to find out how &amp;ldquo;old&amp;rdquo; the computer account is, we will bring in the &lt;b&gt;pwdLastSet&lt;/b&gt; property from Active Directory, in addition to the default values. (Note that the Quest ActiveRoles parameter uses &lt;b&gt;pwdLastSet&lt;/b&gt;, whereas the native Microsoft parameter in tomorrow&amp;rsquo;s blog uses &lt;b&gt;PasswordLastSet&lt;/b&gt;.)&amp;nbsp;This will tell us the last time the hidden password that is negotiated between the computer account and Active Directory has been reset. The default maximum duration is 30 days, so as long as a computer is connecting to the domain regularly, it should always be less than 30 days old.&lt;/p&gt;
&lt;p&gt;If you want to modify the operating systems that get captured, you simply change the selection parameters of the &lt;b&gt;Get-QADComputer&lt;/b&gt; query as shown here:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Get-QADComputer -ComputerRole member -IncludedProperties pwdLastSet -SizeLimit 0 -OSName &amp;#39;Windows 7*&amp;#39; | ForEach-Object { THE REST OF OUR SCRIPT GOES IN HERE }&lt;/p&gt;
&lt;p&gt;Our script will query AD for each &lt;b&gt;Computer&lt;/b&gt; object, and we will run the next bunch of processes against each object in the &lt;b&gt;ForEach-Object&lt;/b&gt; loop. All of the following content is stored inside the curly brackets.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s ignore any failure messages from the IP lookup with this:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;trap [System.Net.Sockets.SocketException] { continue; }&lt;/p&gt;
&lt;p&gt;We need to use the Computer name, DN, and &lt;b&gt;pwdLastSet&lt;/b&gt;, so let&amp;rsquo;s set those as variables from the query result. We also want to capture the current container, so we use a simple &lt;b&gt;Replace&lt;/b&gt; command to derive the current OU location:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ComputerName = $_.Name&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ComputerDN = $_.DN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ComputerPasswordLastSet = $_.pwdLastSet&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ComputerContainer = $ComputerDN.Replace( &amp;quot;CN=$ComputerName,&amp;quot; , &amp;quot;&amp;quot;)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;Now we can work with the Computer account age and delete or move them as necessary:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;# If the computer is more than 90 days off the network, remove the computer object&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;if ($ComputerPasswordLastSet -le $veryold) {&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Remove-QADObject -Identity $ComputerDN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;# Check to see if it is an &amp;quot;old&amp;quot; computer account and move it to the Disabled\Computers OU&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;if ($ComputerPasswordLastSet -le $old) {&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$DestinationDN = $DisabledDN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Move-QADObject -Identity $ComputerDN -NewParentContainer $DestinationDN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p&gt;Next, we query DNS for the IP address of the computer. We will set the &lt;b&gt;$IP&lt;/b&gt; value as $NULL first, so that if the query fails, it will be dealt with correctly later in the process. If we don&amp;rsquo;t set the NULL value, it retains the IP from the last lookup, and it will move the computer incorrectly.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$IP = $NULL&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$IP = [System.Net.Dns]::GetHostAddresses(&amp;quot;$ComputerName&amp;quot;)&lt;/p&gt;
&lt;p&gt;Now it is time to check for the IP range to set the destination DN accordingly. If you have a majority of systems in some network ranges, you may want to move those up to the top of the &lt;b&gt;If&lt;/b&gt; statement so that they are processed early, which will save some time.&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;if ($IP -match $Site1IPRange) {&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $DestinationDN = $Site1DN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;ElseIf ($IP -match $Site2IPRange) {&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $DestinationDN = $Site2DN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;ElseIf ($IP -match $Site3IPRange) {&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $DestinationDN = $Site3DN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Else {&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # If the subnet does not match we should not move the computer so we do nothing&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $DestinationDN = $ComputerContainer&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s do a health check on our IP selection:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/8030.wes_2D00_12_2D00_3_2D00_11_2D00_3.jpg"&gt;&lt;img src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/8030.wes_2D00_12_2D00_3_2D00_11_2D00_3.jpg" alt="Image of command output" title="Image of command output" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And here is the last step to actually move the object to the new destination OU. This is where our NULL IP comes into play because we have assumed that if the IP is NULL, it is &amp;ldquo;off network&amp;rdquo; and the aged account process has already dealt with it:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;if ($IP -ne $NULL) {&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Move-QADObject -Identity $ComputerDN -NewParentContainer $DestinationDN&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;}&lt;/p&gt;
&lt;p&gt;And we made it! Another exciting tip with this script is that you can run all of the &lt;b&gt;QADObject&lt;/b&gt; cmdlets with the &lt;i&gt;WhatIf&lt;/i&gt; parameter, which will output the result to the screen rather than perform the move or delete, so you can test drive the script before you implement it.&lt;/p&gt;
&lt;p&gt;You can download the &lt;a href="http://gallery.technet.microsoft.com/PowerShell-Organize-23c6be16" target="_blank"&gt;script in its full form&lt;/a&gt; from the TechNet Resources Gallery.&lt;/p&gt;
&lt;p&gt;~Eric&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Thank you, Eric. This is a really cool solution to a rather common problem. I am looking forward to part 2 tomorrow from Eric.&lt;/p&gt;
&lt;p&gt;I invite you to follow me on &lt;a href="http://bit.ly/scriptingguystwitter" target="_blank"&gt;Twitter&lt;/a&gt; and &lt;a href="http://bit.ly/scriptingguysfacebook" target="_blank"&gt;Facebook&lt;/a&gt;. If you have any questions, send email to me at &lt;a href="mailto:scripter@microsoft.com" target="_blank"&gt;scripter@microsoft.com&lt;/a&gt;, or post your questions on the &lt;a href="http://bit.ly/scriptingforum" target="_blank"&gt;Official Scripting Guys Forum&lt;/a&gt;. See you tomorrow. Until then, peace.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Ed Wilson, Microsoft Scripting Guy&lt;/b&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.technet.com/aggbug.aspx?PostID=3468202" width="1" height="1" alt="" /&gt;</description></item><item><title>Use PowerShell and Regular Expressions to Parse Handle Output</title><link>http://powershell.com/cs/blogs/hey-scriptingguy/archive/2011/09/16/use-powershell-and-regular-expressions-to-parse-handle-output.aspx</link><pubDate>Fri, 16 Sep 2011 05:00:00 GMT</pubDate><guid isPermaLink="false">f421715f-7aba-45f0-8a8d-44de5318a3a7:12360</guid><dc:creator>Anonymous</dc:creator><description>&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;: Windows PowerShell superhero BATCHman uses regular expressions to parse output from handle.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Microsoft Scripting Guy Ed Wilson here. Today we have Episode 10 of the BATCHman series.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href="http://i.technet.microsoft.com/bb410849.Batchman_Icon_05_bluebatchman_082511(en-us,MSDN.10).png"&gt;&lt;img style="max-width:550px;border:0px;" title="BATCHman and Cmdlet logo" alt="BATCHman and Cmdlet logo" src="http://i.technet.microsoft.com/bb410849.Batchman_Icon_05_bluebatchman_082511(en-us,MSDN.10).png" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Whenever trouble happens in systems and people will call, &lt;br /&gt;and darkness rolls out causing your fall, &lt;br /&gt;Creatures of bits roam in the night, &lt;br /&gt;Shine to the sky, the bright bluish light,&lt;/p&gt;
&lt;p&gt;and call to&amp;hellip;BATCHman !&lt;/p&gt;
&lt;p&gt;&amp;hellip;and, oh yes, his sidekick Boy Blunder Cmdlet, too.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;A smell rolls out from the darkness. An evil smell.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Don&amp;rsquo;t look at me!&amp;rdquo; Cmdlet looks defensively &amp;ldquo;I didn&amp;rsquo;t do it!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The BATCHman looks intensely at his &lt;em&gt;&amp;ldquo;BATCH-o-Meter for Evil Detecting.&amp;rdquo;&lt;/em&gt; &amp;ldquo;No, little buddy, it&amp;rsquo;s the smell of evil afoot. Somewhere in this city, a crime against infrastructure is occurring. I can feel it, I can smell it&amp;hellip;&amp;rdquo;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;***Meanwhile, in a far smellier section of the city, evil is afoot at the Redmond Pocket Protector Recycling Plant.***&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Our backup has failed again! Why??!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Jane the local IT pro/accountant/phone guru/everything else stares at the logs. &amp;ldquo;More than 3,000 files not backed up again! All Word and Excel documents! Why?!!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;In the distance, somewhere within the shadows, a mad cackle can be heard. The sound of a shrieking creature, perhaps a Banshee or just a squirrel that listened to a bit too much disco music. But a horrid sound echoed throughout the building.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Your files are all MINE! MIIIIIIINNNNNEEEE!!! Your backup will never work again! Hee-hee-hee-hee!&amp;rdquo; the sound echoed ominously.&lt;/p&gt;
&lt;p&gt;Out of the corner of her eye, Jane spotted a shadow slither into the darkness. Immediately, she knew what to do. Grabbing her smart phone and popping open Twitter, she pinged the one person that could help her in this dire hour.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;***Meanwhile, in a far less smelly section of the city, evil is &lt;i&gt;not&lt;/i&gt; afoot.***&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Sounds of a budgie echoed throughout the BATCHcave.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Holy shrieking parakeets, BATCHman! It&amp;rsquo;s the BATCHtweet account!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Quickly popping open a module in the BATCHbelt, BATCHman checked for his updates.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;No, I don&amp;rsquo;t want a million dollars. No, I don&amp;rsquo;t want a free crate of Tang. Cmdlet! I was right! There was evil afoot and I&amp;rsquo;m not talking about what you had for lunch! Look.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&lt;em&gt;@JaneITGuru @Batchman! Quick! Help! Backups failing daily! Too many open files! Shrieking! Help me BATCHman!&lt;/em&gt;&lt;i&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Looks like that new Twitter account paid off, BATCHman. Glad I suggested it!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Yes Cmdlet! Quickly! To the BATCHcycles!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Cmdlet stared at the blue tricycle with &amp;ldquo;Cmdlet&amp;ldquo; written on the side in permanent marker. &amp;ldquo;Must we?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;We&amp;rsquo;re a green organization here!&amp;rdquo; cried BATCHman as he mounted his 40-speed carbon fiber, midnight blue special &amp;ldquo;Quickly! Crime waits for no-one!&amp;rdquo;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;***Moments later, Cmdlet is huffing and puffing.***&lt;/strong&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Huff&amp;hellip;puff&amp;hellip;no more&amp;hellip;*gasp*&amp;hellip;Double Downs for&amp;hellip;*ack*&amp;hellip;breakfast!&amp;rdquo; gasped Cmdlet between breaths.&lt;/p&gt;
&lt;p&gt;Jane looked out to greet BATCHman and the heavily sweating Cmdlet at the door. &amp;ldquo;Oh, thank goodness you&amp;rsquo;re here! None of our backups are working on the file server! It&amp;rsquo;s failing because there are documents open when they shouldn&amp;rsquo;t be! Plus there&amp;rsquo;s this noise.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;BATCHman steps off, listening keenly to the air. The shrieking cackling sound could be heard ominously throughout the building.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;I&amp;rsquo;d know that sound anywhere.&amp;rdquo; BATCHman shook his head. &amp;ldquo;That&amp;rsquo;s the Tapeworm! I remember this criminal from an article in &lt;i&gt;BadGuys Weekly&lt;/i&gt;.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;The Tapeworm?&amp;rdquo; Jane queried.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Yes. He was once a brilliant IT professional, but his mind went and snapped one day. He was manning the backups and spent twenty hours restoring an email database from QIC-20 tapes, only to find it was because somebody wanted a cat picture. Since that day, Tapeworm attaches himself to file systems and ruins backups.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Holy, Hello Kitty!&amp;rdquo; burst out Cmdlet &amp;ldquo;I had no idea cats were so evil!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Yes, so what is happening here. I heard &amp;lsquo;failed backups.&amp;rsquo;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Jane gestured to the backup logs indicating failures due to open files.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;We&amp;rsquo;ve even tried selecting the open files in Computer Management, but it takes far too long to enumerate! Plus massive piles of different documents are reopened daily before the backup.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;BATCHman!&amp;rdquo; burst out Cmdlet &amp;ldquo;We&amp;rsquo;ve got to help her get a handle on this!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Yes,&amp;rdquo; the Tilley-hatted one muttered. &amp;ldquo;Funny you should mention &amp;lsquo;Handle&amp;rsquo;. That&amp;rsquo;s the perfect answer.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Poor Cmdlet stared at the floor confused. Deciding to practice telekinesis on his shoelaces was more productive. &amp;ldquo;I don&amp;rsquo;t get it,&amp;rdquo; was all he muttered.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Handle as in Handle.exe from &lt;a title="Sysinternals" href="http://www.sysinternals.com/" target="_blank"&gt;Sysinternals&lt;/a&gt;. We could use that to close the files!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Cmdlet groaned in dismay as yet another horrible joke left BATCHman&amp;rsquo;s lips. &amp;ldquo;Holy PUNishment, BATCHman&amp;mdash;that was horrible!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Our hero smiled with a sly grin. Another well-played play on words.&lt;/p&gt;
&lt;p&gt;Jane looked up. &amp;ldquo;But BATCHman. I already thought of that. We need something that can be automated. That application can&amp;rsquo;t be automated. Normally you run it, it shows the open file Handles in HEX with the process numbers, and then you manually run it again like this to close a file handle.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;HANDLE.EXE &amp;ndash;c 2a3 &amp;ndash;p 5342&lt;/p&gt;
&lt;p&gt;Our hero stood up proudly. &amp;ldquo;For ordinary people and mere mortals, no. But for us, the kings of automation, we have&amp;hellip;&amp;rdquo; he paused ever so dramatically while pulling out a megaphone &amp;ldquo;WINDOWS POWERSHELL!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;While rubbing their ears, they looked at BATCHman equally puzzled. &amp;ldquo;Automate it how?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;BATCHman looked over. &amp;ldquo;Remember the first rule of Windows PowerShell, which is&amp;hellip;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Ooooh! Ooooooooo!&amp;rdquo; Cmdlet jumped up and down like a gerbil on a hot coal &amp;ldquo;I know! Everything is an object!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Exactly! If I ran Handle while in Windows PowerShell and got some output like this&amp;hellip;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/6558.hsg_2D00_9_2D00_16_2D00_11_2D00_1.jpg"&gt;&lt;img style="border:0px;" title="Image of output when running Handle in Windows PowerShell" alt="Image of output when running Handle in Windows PowerShell" src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/6558.hsg_2D00_9_2D00_16_2D00_11_2D00_1.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;rdquo;...we&amp;rsquo;d notice there is a consistent pattern of well-formatted columns. So what we could do is store the output in a Windows PowerShell variable to play with it like this.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ScreenOutput=.\HANDLE.EXE DOCX&lt;/p&gt;
&lt;p&gt;Cmdlet stared at the screen with Jane then burst out. &amp;ldquo;HOLY PATTERN BUFFER BATCHman! Every line has &lt;strong&gt;&amp;lsquo;PID:&amp;rsquo;&lt;/strong&gt; before the Process ID and the word &lt;strong&gt;&amp;lsquo;File&amp;rsquo;&lt;/strong&gt; with a pile of blank spaces before the File Handle! If we could somehow pull out the numbers after PID and FILE we&amp;rsquo;d be halfway there. But we need to clean all the extra stuff out too, like the title.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;BATCHman looked at his sidekick. &amp;ldquo;So what do you think we could do to simplify this output?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Cmdlet rubbed his hands eagerly at the thought of solving the puzzle. &amp;ldquo;I&amp;rsquo;ll wager we could run this output through &lt;b&gt;Select-String&lt;/b&gt; to pull &lt;i&gt;only&lt;/i&gt; the lines that have the &lt;strong&gt;pid:&lt;/strong&gt; field in it!&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ScreenOutput | SELECT-STRING &amp;ndash;pattern &amp;lsquo;pid: &amp;rsquo;&lt;/p&gt;
&lt;p&gt;Cmdlet began to dance a jig like a leprechaun who&amp;rsquo;d gotten away with his box of Lucky Charms. &amp;ldquo;W0000000t! Aha!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;That&amp;rsquo;s good but we can go one step better. With the &lt;strong&gt;\w&lt;/strong&gt; from regular expressions, we can tell the string to find us all letters up to but not counting spaces. We&amp;rsquo;ll add an asterisk, which means all characters following that match this pattern. We can store away the results in a variable to make it easier to work and manipulate.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ProcessIDResults=$ScreenOutput | SELECT-STRING &amp;ndash;pattern &amp;lsquo;pid: [\w]*&amp;rsquo;&lt;/p&gt;
&lt;p&gt;Cmdlet thought. &amp;ldquo;So we could run a similar search for lines beginning with &lt;i&gt;File&lt;/i&gt;, but how do we specify to find all data after that up until the colon character that follows the file Handle?&lt;/p&gt;
&lt;p&gt;BATCHman explained. &amp;ldquo;In regular expressions, you can also specify the pattern of &lt;strong&gt;\s\S&lt;/strong&gt;, which means accept any character including spaces. Doing this we can pull down only the data we need. We can tell it to search for all data beginning with &lt;i&gt;File&lt;/i&gt; and ending with a colon, like this.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$FileHandleResults=$ScreenOutput | SELECT-STRING &amp;ndash;pattern &amp;lsquo;File [s\S]*?:&amp;rsquo;&lt;i&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;Cmdlet looked. &amp;ldquo;So regular expressions can be used in many situations, BATCHman, but how does this help us? We need to get this information back to the Handle program!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Never fear, little chum. Let&amp;rsquo;s see which methods and properties we have available with a &lt;b&gt;Get-Member&lt;/b&gt;.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ProcessIDResults | GET-MEMBER&lt;i&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/8371.hsg_2D00_9_2D00_16_2D00_11_2D00_2.jpg"&gt;&lt;img border="0" alt="" src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/8371.hsg_2D00_9_2D00_16_2D00_11_2D00_2.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;As you can see, like when we battled Dr. Regex, there is a &lt;b&gt;Matches&lt;/b&gt; property. Let&amp;rsquo;s take a look at the data in one of those.&amp;quot;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ProcessIDResults[0].matches[0]&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/0243.hsg_2D00_9_2D00_16_2D00_11_2D00_3.jpg"&gt;&lt;img border="0" alt="" src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/0243.hsg_2D00_9_2D00_16_2D00_11_2D00_3.jpg" width="460" height="177" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;And of course, if we run &lt;b&gt;Get-Member&lt;/b&gt; against this, we can see which methods and properties are available on this object.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;$ProcessIDResults[0].matches[0] | GET-MEMBER&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/4011.hsg_2D00_9_2D00_16_2D00_11_2D00_4.jpg"&gt;&lt;img border="0" alt="" src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/4011.hsg_2D00_9_2D00_16_2D00_11_2D00_4.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Cmdlet&amp;rsquo;s eyes lit up when he saw &lt;b&gt;tostring()&lt;/b&gt; as a method. He knew from his previous experiences with string objects in Windows PowerShell there was a &lt;b&gt;substring()&lt;/b&gt; method available.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;BATCHman! We could convert each match to a string! And because we know the data is from the &lt;b&gt;Matches&lt;/b&gt; object, we could run a &lt;b&gt;substring()&lt;/b&gt; on the output!&amp;rdquo; Cmdlet barked out like a chihuahua.&lt;/p&gt;
&lt;p&gt;Before BATCHman could reply, a loud blaring klaxon went off. Flashing neon lights lit the city streets!&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Oh, no!&amp;rdquo; burst out BATCHman. &amp;ldquo;That&amp;rsquo;s the alarm on the WinMobile! Somebody&amp;rsquo;s gotten to it!&amp;rdquo; as he barreled down the stairs.&lt;/p&gt;
&lt;p&gt;Will BATCHman find the WinMobile intact? &lt;br /&gt;Will Cmdlet finalize the solution for Handle? &lt;br /&gt;Will Tapeworm ever get a speaking part? &lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;Find out at the same BATCHtime, same BATCHchannel in the next episode of BATCHman and Cmdlet!&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I invite you to follow me on &lt;a href="http://bit.ly/scriptingguystwitter" target="_blank"&gt;Twitter&lt;/a&gt; and &lt;a href="http://bit.ly/scriptingguysfacebook"&gt;Facebook&lt;/a&gt;. If you have any questions, send email to me at &lt;a href="mailto:scripter@microsoft.com" target="_blank"&gt;scripter@microsoft.com&lt;/a&gt;, or post your questions on the &lt;a href="http://bit.ly/scriptingforum" target="_blank"&gt;Official Scripting Guys Forum&lt;/a&gt;. See you tomorrow. Until then, peace.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Ed Wilson, Microsoft Scripting Guy&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.technet.com/aggbug.aspx?PostID=3453085" width="1" height="1" alt="" /&gt;</description></item><item><title>BATCHman Uses PowerShell to Thwart Dr. Regex and Save the Zoo</title><link>http://powershell.com/cs/blogs/hey-scriptingguy/archive/2011/09/02/batchman-uses-powershell-to-thwart-dr-regex-and-save-the-zoo.aspx</link><pubDate>Fri, 02 Sep 2011 05:00:00 GMT</pubDate><guid isPermaLink="false">f421715f-7aba-45f0-8a8d-44de5318a3a7:12103</guid><dc:creator>Anonymous</dc:creator><description>&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;: In this exciting episode, BATCHman uses Windows PowerShell and regular expressions to thwart the evil Dr. Regex and save the monkeys.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Microsoft Scripting Guy, Ed Wilson, is here. Sean Kearney is back with us for three more episodes of the thrilling BATCHman series. Today is Episode 4.&lt;/p&gt;
&lt;p&gt;&lt;img style="max-width:550px;" border="0" alt="BATCHman and Cmdlet logo" src="http://i.technet.microsoft.com/bb410849.Batchman_Icon_05_bluebatchman_082511(en-us,MSDN.10).png" /&gt;&lt;/p&gt;
&lt;p&gt;Whenever trouble happens in systems and people will call, &lt;br /&gt;and darkness rolls out causing your fall, &lt;br /&gt;Creatures of bits roam in the night, &lt;br /&gt;Shine to the sky, the bright bluish Light,&lt;/p&gt;
&lt;p&gt;and call to&amp;hellip;BATCHman !&lt;/p&gt;
&lt;p&gt;&amp;hellip;and, oh yes, his sidekick Boy Blunder Cmdlet, too.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;In Today&amp;rsquo;s Episode, BATCHman Encounters Doctor Regex!&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Cutting through the midnight blue sky, a beacon for justice, a light for freedom calls out!&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Holy shell, BATCHman! It&amp;rsquo;s the BATCHsignal! We have&amp;hellip;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Ahem, Cmdlet, I would suggest you watch your language there. This is after all a &amp;lsquo;G&amp;rsquo; rated blog post.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;But but, I&amp;hellip;I&amp;hellip;ugh&amp;hellip;yes, BATCHman,&amp;rdquo; he sullenly bowed in defeat. &amp;ldquo;Shall I fire up the WinMobile?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;No, but you can start it,&amp;rdquo; snickered our hero. &amp;ldquo;To the WinMobile!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Buckling under the weight of our heroes, the WinMobile moved off. &amp;ldquo;Thank goodness it&amp;rsquo;s made of Gorilla Glass,&amp;rdquo; muttered Cmdlet.&lt;/p&gt;
&lt;p&gt;Meanwhile, somewhere else in the city of Redmond, evil has placed a call. The arch criminal simply known as the dreaded Dr. Regex has visited. Cursed after too many nights of writing parse queries, our arch villain only speaks in riddles of Regex. He has taken the team at the local Redmond Zoo by surprise. All the cages are being unlocked, potentially unleashing the vicious GUI eating SAPIEN monkeys! Our arch villain dances off on top of the cages laughing maniacally in an oddly cheeseburger colored jumpsuit with his big &amp;ldquo;!&amp;rdquo; symbol. &amp;ldquo;Muah ha haaaaa!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The only passcode to the electronic Chubb lock system has been left somewhere in this cryptic riddle left on Notepad on the desktop of the main server:&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Evil may Come And Evil May Go &lt;br /&gt;The BATCHman Will Seek, be Fast Or Be Slow &lt;br /&gt;But Soon On the Day The pass It Must Be &lt;br /&gt;The word Should He Say, To Not Set Them Free &lt;br /&gt;is The BATCHman Too Smart, Or Is He Not Bright? &lt;br /&gt;under The Stars And Under the Light! &lt;br /&gt;The key We Shall See, Unlocking This Clue &lt;br /&gt;Will chain Them All Down, Or Let Them Run Through&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Signed ^[Dr!Regex]^&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/1033.HSG_2D00_9_2D00_2_2D00_11_2D00_2.jpg"&gt;&lt;img style="border:0px;" title="Photo of the evil Dr. Regex" alt="Photo of the evil Dr. Regex" src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/1033.HSG_2D00_9_2D00_2_2D00_11_2D00_2.jpg" width="200" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;BATCHman and Cmdlet arrive on the scene, the sound of Monkeys about to tear scripts apart. Cmdlet stared at the riddle. &amp;ldquo;Holy twisted blisters, BATCHman! That must have worn out somebody&amp;rsquo;s fingers typing! What a bizarre rid&amp;hellip;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;&amp;hellip;.bizarre clue!&amp;rdquo; corrected BATCHman &amp;ldquo;Yes. Yes, indeed. What a strange person. There must be some type of pattern in this clue. Something usual to the naked human eye, something I can&amp;rsquo;t quite see, some&amp;hellip;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;BATCHman! It appears only certain words start with a lowercase letter!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Cmdlet! Excellent ! How did you spot it so quickly?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;It&amp;rsquo;s written here in the plot, page 5, line 7&amp;hellip;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;BATCHman looks down scratching his cowl. &amp;ldquo;Ah, so it is.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;So we could just write down every word,&amp;rdquo; suggested Cmdlet.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Pah! And waste a good chance to use Windows PowerShell? Never! This is a perfect opportunity to use regular expressions to find those matches! First, we need to identify the pattern. We need all words starting with a lowercase letter only. Within regular expressions, that would be a matter of specifying that the first letter is from &lt;i&gt;a&lt;/i&gt; to &lt;i&gt;z&lt;/i&gt; only like this.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;[a-z]&lt;/p&gt;
&lt;p&gt;&amp;ldquo;And then we&amp;rsquo;ll need to specify it&amp;rsquo;s at the beginning of the word.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;\b[a-z]&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Then we&amp;rsquo;ll specify that the word must be at least two letters long if we find it, but can be as long as we want. This should trap our series of words.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;\b[a-z]{2,}&lt;/p&gt;
&lt;p&gt;&amp;ldquo;With this in place, Cmdlet, we need to pipe the information from the text file into a &lt;b&gt;Select-String&lt;/b&gt; statement like this.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;GET-CONTENT C:\EVILCLUE.TXT | SELECT-STRING &amp;lsquo;\b[a-z]{2,}&amp;rsquo;&lt;/p&gt;
&lt;p&gt;Cmdlet sat there cross-legged watching the magic unfold. &amp;ldquo;But how do we tell it to look for more than one instance, BATCHman? How does it know it is lowercase?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Presently it doesn&amp;rsquo;t, but it will if we add more parameters to our &lt;b&gt;Select-String&lt;/b&gt;. One is -&lt;i&gt;allmatches&lt;/i&gt; in case there is more than one instance per line. The other is -&lt;i&gt;casesensitive&lt;/i&gt; because &lt;b&gt;Select-String&lt;/b&gt; by default just does a &lt;i&gt;&amp;ndash;match&lt;/i&gt; which doesn&amp;rsquo;t care about uppercase or lowercase.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;GET-CONTENT C:\EVILCLUE.TXT | SELECT-STRING &amp;lsquo;\b[a-z]{2,}&amp;rsquo; &amp;ndash;casesensitive &amp;ndash;allmatches&lt;/p&gt;
&lt;p style="padding-left:60px;"&gt;Evil may Come And Evil May Go &lt;br /&gt;The BATCHman Will Seek, be Fast Or Be Slow &lt;br /&gt;But Soon On the Day The pass It Must Be &lt;br /&gt;The word Should He Say, To Not Set Them Free &lt;br /&gt;is The BATCHman Too Smart, Or Is He Not Bright? &lt;br /&gt;under The Stars And Under the Light! &lt;br /&gt;The key We Shall See, Unlocking This Clue &lt;br /&gt;Will chain Them All Down, Or Let Them Run Through&lt;/p&gt;
&lt;p&gt;Cmdlet looked and laughed. &amp;ldquo;Holy foot in mouth! The output is the same!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The BATCHman smiled. &amp;ldquo;Not quite. If we run &lt;b&gt;Get-Member&lt;/b&gt; against this output, you&amp;rsquo;ll see something different.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;GET-CONTENT C:\EVILCLUE.TXT | SELECT-STRING &amp;lsquo;\b[a-z]{2,}&amp;rsquo; &amp;ndash;casesensitive &amp;ndash;allmatches | GET-MEMBER&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/3580.HSG_2D00_9_2D00_2_2D00_11_2D00_3.jpg"&gt;&lt;img border="0" alt="" src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/3580.HSG_2D00_9_2D00_2_2D00_11_2D00_3.jpg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;This, Cmdlet, is not just a string. We can see there are properties such as &lt;i&gt;Matches&lt;/i&gt;. If we were to look at the properties of &lt;i&gt;Matches&lt;/i&gt;, we mind find something interesting.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;GET-CONTENT C:\EVILCLUE.TXT | SELECT-STRING &amp;lsquo;\b[a-z]{2,}&amp;rsquo; &amp;ndash;casesensitive &amp;ndash;allmatches | SELECT-OBJECT Matches&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blogs.technet.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/2337.HSG_2D00_9_2D00_2_2D00_11_2D00_4.jpg"&gt;&lt;img style="border:0px;" title="Image of matches" alt="Image of matches" src="http://blogs.technet.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-00-76-18/2337.HSG_2D00_9_2D00_2_2D00_11_2D00_4.jpg" width="150" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Holy obvious! Do you mean&amp;hellip;&amp;rdquo; looking to his left Cmdlet lifts the keychain to the old cages. A Big envelope with the words &amp;ldquo;^[Dr!Regex]^&amp;rdquo; is written on it. He bites it open and his mouth drops sullenly.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;What is it, Cmdlet?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Another clue. Awww, come &lt;i&gt;on&lt;/i&gt;!!!&amp;rdquo; Cmdlet stomps about like a little child having a hissy fit showing the new clue to BATCHman.&lt;/p&gt;
&lt;p&gt;Remove ME now and unlock the clue&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;lsquo;meHeymeheymewemearemethemescriptmemonkeesmeandmepeoplemesaymewemearemescriptingmeawayme&amp;rsquo;&lt;/p&gt;
&lt;p&gt;BATCHman looks. &amp;ldquo;I&amp;rsquo;m seeing an awful lot of &lt;i&gt;ME&lt;/i&gt; in this particular line. I will bet we could just use a &lt;i&gt;&amp;ndash;replace&lt;/i&gt; on it!&lt;/p&gt;
&lt;p&gt;&amp;ldquo;What&amp;rsquo;s that?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Seating himself down in front of the Windows PowerShell console, he explains. &amp;ldquo;&lt;i&gt;Replace&lt;/i&gt; is another part of regular expressions where we can replace one thing with another.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;&amp;lsquo;meHeymeheymewemearemethemescriptmemonkeesmeandmepeoplemesaymewemearemescriptingmeawayme&amp;rsquo; &amp;ndash;replace &amp;lsquo;me&amp;rsquo;,&amp;rsquo;&amp;rsquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;That will replace every reference of &lt;b&gt;me&lt;/b&gt; with nothing, which gives us this result.&amp;rdquo;&lt;/p&gt;
&lt;p style="padding-left:30px;"&gt;Heyheywearethescriptmonkeesandpeoplesaywearescriptingaway&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Cmdlet, please try this password on the Chubb security system. I&amp;rsquo;m sure you&amp;rsquo;ll find it works; just press the &lt;b&gt;Lock All Cages&lt;/b&gt; button.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Soon echoing through the Redmond Zoo, massive sounds of &lt;b&gt;*KLANG*&lt;/b&gt; echo throughout with the odd *&lt;b&gt;yelp&lt;/b&gt;* as the occasional finger gets pinched on closing doors.&lt;/p&gt;
&lt;p&gt;With one loud *&lt;b&gt;THUD&lt;/b&gt;*, Dr. Regex falls into bushes below. &amp;ldquo;Waaaaihihghghhgih!!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The keeper of the Redmond Zoo looks up. &amp;ldquo;Oh, BATCHman! How can ever thank you?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Easy, good citizen. One, do not put your master security password under a set of keys to the Monkey House and&amp;hellip;do you have something I could scrape my shoes on? I think I stepped on something near the cages.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Thanks, Sean, for another exciting episode of BATCHman! Join us tomorrow for Episode 5.&lt;/p&gt;
&lt;p&gt;I invite you to follow me on &lt;a href="http://bit.ly/scriptingguystwitter" target="_blank"&gt;Twitter&lt;/a&gt; and &lt;a href="http://bit.ly/scriptingguysfacebook"&gt;Facebook&lt;/a&gt;. If you have any questions, send email to me at &lt;a href="mailto:scripter@microsoft.com" target="_blank"&gt;scripter@microsoft.com&lt;/a&gt;, or post your questions on the &lt;a href="http://bit.ly/scriptingforum" target="_blank"&gt;Official Scripting Guys Forum&lt;/a&gt;. See you tomorrow. Until then, peace.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Ed Wilson, Microsoft Scripting Guy&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.technet.com/aggbug.aspx?PostID=3449490" width="1" height="1" alt="" /&gt;</description></item><item><title>Use PowerShell Regular Expressions to Parse an RSS Feed</title><link>http://powershell.com/cs/blogs/hey-scriptingguy/archive/2011/03/04/use-powershell-regular-expressions-to-parse-an-rss-feed.aspx</link><pubDate>Fri, 04 Mar 2011 06:00:00 GMT</pubDate><guid isPermaLink="false">f421715f-7aba-45f0-8a8d-44de5318a3a7:9559</guid><dc:creator>Anonymous</dc:creator><description>Summary : Learn how to use Windows PowerShell regular expressions to parse an RSS feed. Hey, Scripting Guy! How can I find patterns in text with regular expressions when I am unsure of where line breaks will occur in the text I am reading? &amp;mdash;TT Hello...(&lt;a href="http://blogs.technet.com/b/heyscriptingguy/archive/2011/03/04/use-powershell-regular-expressions-to-parse-an-rss-feed.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://blogs.technet.com/aggbug.aspx?PostID=3390005" width="1" height="1" alt="" /&gt;</description></item><item><title>Use PowerShell Regular Expressions to Format Numbers</title><link>http://powershell.com/cs/blogs/hey-scriptingguy/archive/2011/03/03/use-powershell-regular-expressions-to-format-numbers.aspx</link><pubDate>Thu, 03 Mar 2011 06:00:00 GMT</pubDate><guid isPermaLink="false">f421715f-7aba-45f0-8a8d-44de5318a3a7:9553</guid><dc:creator>Anonymous</dc:creator><description>Summary : Microsoft MVP, Tome Tanasovski, shows how to use regular expression lookaheads and lookbehinds in Windows PowerShell to format numbers. Hey, Scripting Guy! How do I use a regular expression to add the appropriate commas to a number? &amp;mdash;TT...(&lt;a href="http://blogs.technet.com/b/heyscriptingguy/archive/2011/03/03/use-powershell-regular-expressions-to-format-numbers.aspx"&gt;read more&lt;/a&gt;)&lt;img src="http://blogs.technet.com/aggbug.aspx?PostID=3389997" width="1" height="1" alt="" /&gt;</description></item></channel></rss>