Posts RSS Comments RSS 253 Posts and 411 Comments till now

Implementing XenDesktop 4.0 on Hyper-V R2

Citrix and Microsoft have teamed up to offer the “Implementing XenDesktop 4.0 on Hyper-V R2” virtual lab, an interactive, pre-built environment/training session for testing and viewing the XenDesktop 4 & Hyper-V solution. Check it out!

For details and a video, click here:
http://community.citrix.com/x/aonqBg

To launch the virtual lab, click here:
https://cmg.vlabcenter.com/default.aspx?moduleid=281742e3-2613-42da-bd58-2c3578f039b4

Re-Awarded the Microsoft MVP for Powershell.

I was very happy to get the email today. We (MVPs) generally feel we do enough but we are always concerned that MS thinks so 🙂

I would also like to welcome “Mr. Antoine Habert” to the Powershell MVP ranks. I *think* he is the only new one and I don’t believe we lost any.

ADMG (aka ADWS for none 2008 R2 )

As you may or may not know the AD cmdlets that ship with Win7 and Windows 2008 R2 use the ADWS (Active Directory Web Service) but fear not! MS release ADMG (Active Directory Management Gateway) that allows you to use the AD cmdlets and ADAC (Active Directory Administrative Center)

Download here: http://support.microsoft.com/default.aspx?scid=kb;en-us;969041&sd=rss&spid=12925

For more information about Active Directory Web Services: http://technet.microsoft.com/en-us/library/dd391908.aspx

For more information about the Active Directory Module for Windows PowerShell: http://technet.microsoft.com/en-us/library/dd378937.aspx

The Annual Scripting Games are approaching

Get out your favorite IDE and get to coding 🙂

PowerShellCommunity.org Joins Forces with Microsoft Scripting Guys to Host 2009 Summer Scripting Games

Scripting Games, June 15–26, 2009.

Ever wanted to have a CLI version of Windows Update?

I recently started building several dozen Server Core machines and as part of the process I needed to run configure and run Windows update. After some google'ing I found this Searching, Downloading, and Installing Updates. It was VERY useful and got me thinking. How could I use Powershell to make this simpler and easier?

Below you will find the script I came up with
  1. # Get-WindowsUpdate.ps1
  2. Param([switch]$download,[Switch]$install,[switch]$FullDetail,[switch]$confirm)
  3. function Get-WIAStatusValue($value)
  4. {
  5. switch -exact ($value)
  6. {
  7. 0 {"NotStarted"}
  8. 1 {"InProgress"}
  9. 2 {"Succeeded"}
  10. 3 {"SucceededWithErrors"}
  11. 4 {"Failed"}
  12. 5 {"Aborted"}
  13. }
  14. }
  15.  
  16. Write-Host
  17.  
  18. Write-Host " - Creating WU COM object"
  19. $UpdateSession = New-Object -ComObject Microsoft.Update.Session
  20. $UpdateSearcher = $UpdateSession.CreateUpdateSearcher()
  21.  
  22. Write-Host " - Searching for Updates"
  23. $SearchResult = $UpdateSearcher.Search("IsAssigned=1 and IsHidden=0 and IsInstalled=0")
  24.  
  25. if($Download -or $Install -or $Confirm)
  26. {
  27. Write-Host " - Found [$($SearchResult.Updates.count)] Updates to Download."
  28.  
  29. # Get Total Download size
  30. $Total = ($SearchResult.Updates | measure-Object -sum MaxDownloadSize).Sum
  31. $Current = 0
  32.  
  33. Write-Host (" - Total of {0:n2} MB to Download." -f ($Total/1mb))
  34. Write-Host
  35.  
  36.  
  37. foreach($Update in $SearchResult.Updates)
  38. {
  39. write-progress $Update.Title "Total Progress->" -percentcomplete ($Current/$Total*100)
  40. $Size = "{0:n2}MB" -f ($Update.MaxDownloadSize/1mb)
  41. if($Confirm)
  42. {
  43. $caption = "Get-WindowsUpdate: $($Update.Title) [$Size]"
  44. $message = "Download/Install Update?"
  45. $yes = new-Object System.Management.Automation.Host.ChoiceDescription "&Yes;"
  46. $no = new-Object System.Management.Automation.Host.ChoiceDescription "&No;"
  47. $choices = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no)
  48. $answer = $host.ui.PromptForChoice($caption,$message,$choices,0)
  49. if($answer -eq 1){$Current += $Update.MaxDownloadSize;continue}
  50. }
  51.  
  52. # Check for Eula and Accept in needed
  53. if ( $Update.EulaAccepted -eq 0 ) { $Update.AcceptEula() }
  54. # Add Update to Collection
  55.  
  56. $UpdatesCollection = New-Object -ComObject Microsoft.Update.UpdateColl
  57. $UpdatesCollection.Add($Update) | out-null
  58.  
  59. # Download
  60. Write-Host " + Downloading Update $($Update.Title) [$Size]"
  61. $UpdatesDownloader = $UpdateSession.CreateUpdateDownloader()
  62. $UpdatesDownloader.Updates = $UpdatesCollection
  63. $DownloadResult = $UpdatesDownloader.Download()
  64. $Message = " - Download {0}" -f (Get-WIAStatusValue $DownloadResult.ResultCode)
  65. Write-Host $message
  66.  
  67. # Install
  68. if($install)
  69. {
  70. Write-Host " - Installing Update"
  71. $UpdatesInstaller = $UpdateSession.CreateUpdateInstaller()
  72. $UpdatesInstaller.Updates = $UpdatesCollection
  73. $InstallResult = $UpdatesInstaller.Install()
  74. $Message = " - Install {0}" -f (Get-WIAStatusValue $DownloadResult.ResultCode)
  75. Write-Host $message
  76. Write-Host
  77. }
  78. $Current += $Update.MaxDownloadSize
  79. }
  80. }
  81. else
  82. {
  83. Write-Host " - Found [$($SearchResult.Updates.count)] Updates."
  84. Write-Host (" - Total of {0:n2} MB to Download." -f ($Total/1mb))
  85. Write-Host
  86. if($FullDetail)
  87. {
  88. $SearchResult.Updates
  89. }
  90. else
  91. {
  92. $SearchResult.Updates | %{$_.Title}
  93. }
  94. }

Proof MS is betting BIG on Powershell

If you haven’t figure this out yet or somehow missed it on my blog. MS is putting a HUGE amount of effort towards Powershell. We are talking dozens and dozens of groups at MS working with Powershell.

Just to let you know how important it is to Microsoft… Check this out

Jeffrey gets Distinguished Engineer

That may not mean much to you… but that is HUGE. There not many (like 36) Distinguished Engineers at MS. It is a huge honor and a testament to the value MS puts in Jeffrey and on Powershell.

More Powershell RegEx fun (playing with ‘route Print’)

A problem come that required me to check the routing table on a large number of machines. My first thought was to try to find a .NET class to dump the table, but alas I could not find any class to do what I wanted. That was a bummer but not unexpected so I decided to just use ol’faithful psexec.exe (Found here.)

Now, for those of you that have used ‘route print’ before you know the output is well… verbose 🙂 and clearly that would not be very helpful for filtering.


Enter ‘Get-RouteTable” (found below)

I share this, not so much because I found it incredibly useful (although I did,) but because it illustrates what I have found to be a great way of creating complex Regular Expressions (for the sake of brevity I will use RegEX for the rest of the article.)

My approach is quiet simple. Create a string object for the RegEx and build the Regex over time testing each section.

Lets take this string for example

Active Routes:
Network Destination        Netmask                 Gateway          Interface            Metric
          0.0.0.0                   0.0.0.0                    192.168.1.1     192.168.1.13     25
        127.0.0.0                 255.0.0.0                127.0.0.1          127.0.0.1           306
        127.0.0.1                 255.255.255.255    127.0.0.1           127.0.0.1           306

I wanted to extract the Network Destination, Netmask, Gateway, Interface, and Metric and make them properties on a custom object. How does one go about that? One way is to create a regular expression with labels and use $matches to extract the information. This is route I took, but instead of trying to write a single complex RegEx and then trouble-shooting it for hours I decided to break down the regular expression into bite size chunks.

To show you what I mean by complex… here is the RegEx you end up with

“^\s+(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<Gateway>\d{1,3
}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<Metric>\d{1,4})$”

As you can this is fairly complex and could be a HUGE pain to trouble shoot.

Following my approach you break down. First you add the begin and end to the regex

$DiscoverRoute  = “^\s+”

$DiscoverRoute += “.+$”

The next step is we add a label and pattern for the “Network Destination” and test

$DiscoverRoute  = “^\s+”

$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”

$DiscoverRoute += “.+$”

(route print) | ?{$_ -match $DiscoverRoute}

If you get results you expect the next step is to make sure $matches sees the labels as you expect

(route print) | ?{$_ -match $DiscoverRoute} | %{$matches}

Great! Now we can the next part

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “.+$”

Repeat the Last test but make sure you have a value for both Network and NetMask

(route print) | ?{$_ -match $DiscoverRoute} | %{$matches}

Rinse and repeat until you end up with this

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Gateway>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Metric>\d{1,4})”
$DiscoverRoute += “.+$”

This does not work just yet… can you spot why? The reason is because we have been adding “.+$” (zero or more of anything) to the end every time for testing purposes. We now need to remove this and add the $ to the end of the Metric string because the EOL is immediately after the last digit in Metric column. We end up with this

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Gateway>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Metric>\d{1,4})$”

We took a complex RegEx and with a little string concantination we were able to break it down and make the task quite simple.

Note: Another cool effect of this method is that we can easily comment out a section that maybe break the entire regular expression like this

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
#$DiscoverRoute += “(?<Gateway>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “.+(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Metric>\d{1,4})$”

Just make sure that you add the “.+” to the line just after the comment line. This allows the regular expression to replace that section with basically anything.

NOTE: A final, slightly embarrassing, note. After writing the script below I realized this information (save the persistent routes) is readily available using WMI via the Win32_IP4RouteTable class.

The Code:


Param($Servers)

function Convert-RouteTabletoObject ($data)
{
    # RegEx for Discovery Routes
    $DiscoverRoute  = "^\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,4})"
    $DiscoverRoute += "$"

    # RegEx for Persistent Routes
    $PersistentRoute  = "^\s+"
    $PersistentRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $PersistentRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $PersistentRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $PersistentRoute += "(?\d{1,4})"
    $PersistentRoute += "$"

    $Routes = @()
    switch -regex ($data)
    {
        $DiscoverRoute
                    {
                        $NewRoute = "" |Select-Object Network,Netmask,Gateway,Interface,Metric,Persistent
                        $NewRoute.Network = $matches.Network
                        $NewRoute.Netmask = $matches.Netmask
                        $NewRoute.Gateway = $matches.Gateway
                        $NewRoute.Interface = $matches.Interface
                        $NewRoute.Metric = $matches.Metric
                        $NewRoute.Persistent = $False
                        $Routes += $NewRoute
                    }
        $PersistentRoute
                    {
                        $NewRoute = "" |Select-Object Network,Netmask,Gateway,Interface,Metric,Persistent
                        $NewRoute.Network = $matches.Network
                        $NewRoute.Netmask = $matches.Netmask
                        $NewRoute.Gateway = $matches.Gateway
                        $NewRoute.Interface = "N/A"
                        $NewRoute.Metric = $matches.Metric
                        $NewRoute.Persistent = $True
                        $Routes += $NewRoute
                    }
    }
    $Routes
}

foreach($server in $servers)
{
    $myobj = "" | Select-Object Name,RouteTable
    $myobj.Name = $Server
    $myobj.RouteTable = Convert-RouteTabletoObject (invoke-Expression "psexec \\$server route print" 2&gt;$NULL)
    $myobj
}

# Note: You can use WMI to get most of this info – Get-WMIObject Win32_IP4RouteTable -ComputerName

Regular Expression Info and Tips for Powershell

One of the most useful tools a scripter has at their disposal is Regular Expressions. The problem is that regular expressions can seem like Greek and for most folks learning them can seem an impossible task. I hope this blog post provides those people some hope as well as some tools they can use to tackle this mountain one foot at a time.

NOTE: I would like to make this blog entry as dynamic as possible, so if you have your own tips… let me know and I will add them.


Everyone is different, but this is how I went about learning Regular Expressions

  • Phase one: Learn EXACTLY what a regular expression is and what it can and can NOT do.
  • Phase two: Learn the syntax. I printed out the RegEx Cheet Sheet and use it daily.
  • Phase three: Find a purpose. I find if I actually have a problem I spend more energy on learning.
  • Phase four: Create a simple Regular Expression.
  • Phase five: Build on your Regular Expression.
  • Phase six: Trouble shooting the Regular Expression.

Useful Tips:

  • First things first… Click here and watch the vids
  • Keep a cheat sheet close at hand!
  • Take them in small bites. I hope to blog in detail about this later, but lets just say string concatenation is your friend
  • Keep it simple!
  • $matches is your friend! $matches is an object that Powershell creates when you make a RegEx call (like -match.)
  • Google It. Chances are there is Regex already out there for what you need.
  • Using Lables can save you time by extracting certain data and making it a property on $matches.
  • RichardP from IRC says “Dont eat yellow snow?” Apparently he has experience in this area.

Online Vids:
This is GREAT stuff provided by Shay (aka scriptfanatic)

Good Blog Entries:
MoW at it again.

References:
RegEx Cheet Sheet (my Favorite)
Regular-Expressions.info
Regular Expression Library

Online Regex Checkers:
RegEx Tester (my Favorite)
Regular Expression Library Tester

Books:
Mastering Regular Expressions (my Favorite)
Regular Expression Recipes for Windows Developers

Tools:
Expresso
RegEx Buddy

Constructed Properties and LVR (Linked-Value Replication)

There was an interesting question that came up on the news group that discussed getting Active Directory replication metadata and while the question really wasn’t directly a Powershell question I found it intriguing. I knew I had the answer to the question back in my brain, but I couldn’t retrieve it.

Here is the Question:

“I’m working on a script where I need to compare the last time the member
attribute of a distribution group was modified (not the AD group object
itself) with the time stamp on a file (I’m exporting distribution group
memberships to a file, but only ones that have changed). The problem
I’m running into is that some Active Directory distribution groups
aren’t returning the member attribute when I look at the replication
meta data.

Any one have any thoughts on why some distribution groups return the
member attribute when I run GetReplicationMetadata and some don’t? This
returns all kinds of other attributes and their metadata, but naturally
not the one I’m interested in (it does some times, on some groups, but
not all). I could run my script by the actual AD object WhenChanged
attribute, but I’ll be processing a large number of lists and I want it
to run as fast as possible and since other attributes can change on a
group object, I don’t want to have to export a 8,000 member group if the
displayName changes, for example (I’m only interested in the member
attribute).
As always, any insight is appreciated.”

I thought about this for a bit and also consulted some AD friends and we determined the issue was LVR (Link Value Replication.) This was introduced in Windows 2003 (specifically 2003 Native Mode.)

Basically, LVR changed the unit of replication for some attributes to be the actual values. Prior to LVR if you changed group membership the entire attribute member would have to replicate. With LVR, just the link you added for user replicates.

As one can imagine this changed the Metadata and therefore GetReplicationMetadata() didnt get retrieve the data for you. Where does this leave us?

There were also a few constructed attributes that were added with 2003. One of which is called “msds-replvaluemetadata.” This attribute provided the metadata for these links in wonderful XML format. You will find code using this XML below.

Some Useful Links regarding LVR and Constructed Attributes:

Below is the script that resulted from the investigation


 

Get-GroupMemberMetadata.ps1

Param($GroupName)

$GroupMembers = @()

$root = [ADSI]""
$filter = "(&(objectclass=group)(name=$GroupName))"
$props = @("msDS-ReplValueMetaData")
$Searcher = new-Object System.DirectoryServices.DirectorySearcher($root,$filter,$props)
foreach($Group in $Searcher.findall())
{
    foreach($childMember in $Group.Properties."msds-replvaluemetadata")
    {
        $GroupMembers += [xml+site:msdn.microsoft.com”>XML]$ChildMember
    }
}

foreach($Member in $GroupMembers)
{
    $Member.ChildNodes
}

MS Cluster fun with Powershell (super cool)

A little while back Brad and I wrote a script to compare files between Microsoft Clusters.

After a little back and forth we came up with the script posted here: Check that driver file versions match on all your cluster nodes via Powershell

It is very cool.

Next »