Friday, January 30, 2009

Compare-Object –SyncWindow parameter

I was comparing two lengthy configuration files (XML) knowing ahead of time that there were only a couple differences between them. The code I used was as follows:

$a = Get-Content file1.xml
$b = Get-Content File2.xml
Compare-Object $a $b

My result set was not at all what I expected. Instead of being a few lines it was almost the size as my two files combined! I was confused (and a little hungry). Turning to Get-Help compare-object -full, I saw this:

-syncWindow (int)
Defines a search region where an attempt is made to re-sync the order if there is no match.

Required? false
Position? named
Default value 5
Accept pipeline input? false
Accept wildcard characters? false

I must admit that I did not find this intuitive. Looks like by default it will look 5 lines up and 5 lines down for a match before moving on. Grabbing a Snickers and Diet Coke, I took my newly acquired knowledge and tried the following:

$a = Get-Content file1.xml
$b = Get-Content File2.xml
Compare-Object $a $b -SyncWindow 50

Success!! Fat and happy, I now have the results I expected.

InputObjectSideIndicator
driver-configuration dn="....""=>"
shim-auth-id value="""=>"

Enjoy!

Saturday, January 24, 2009

Project Euler and PowerShell - Problem 19

Eleven down, 14 more to go before I reach level 1 (tetrahedron) on Project Euler. This one was easy if you know what PowerShell can do with dates!

How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)?

$startDate = Get-Date "01/01/1901"
$endDate = Get-Date "12/31/2000"
$cnt = 0
for($date=$startDate; $date -lt $endDate; $date = $date.AddMonths(1))
{
if ($date.DayOfWeek -eq 'Sunday')
{
$cnt++
}
}
$cnt

Wednesday, January 14, 2009

PowerShell and Exchange 2003

Was tasked with getting some information out of Exchange 2003. Basically they wanted me to match up mailbox accounts with AD Users.  The only consistent way to do this in our environment was to compare the LegacyDN property from the root\MicrosoftExchangeV2 - Exchange_Mailbox class with the AD User property LegacyExchangeDN.  Following is the script used to iterate through our Exchange 2003 servers and generate an Excel report (CSV file).








  1. function Get-ExchangeInfo   

  2. {   

  3.     BEGIN   

  4.     {    

  5.         $Start = Get-Date  

  6.         Write-Host "Script Started: $start`nProcessing"  

  7.     }      

  8.     PROCESS   

  9.     {   

  10.         $MailBoxes = Get-WmiObject `   

  11.           -namespace 'root\MicrosoftExchangeV2' `   

  12.           -class 'Exchange_MailBox' `   

  13.           -computerName $_ | sort size -desc | `   

  14.            Select MailBoxDisplayName, `   

  15.             ServerName, `   

  16.             StorageGroupName, `   

  17.             StoreName, `   

  18.             Size, `   

  19.             TotalItems, `   

  20.             legacyDN   

  21.                

  22.         Write-Host "Server: $_"  

  23.         foreach($MailBox in $MailBoxes)   

  24.         {      

  25.             $ldap = "(LegacyExchangeDN={0})" -f $MailBox.legacyDN   

  26.             $User = Get-QADUser `   

  27.               -SizeLimit 0 `   

  28.               -IncludeAllProperties `   

  29.               -LdapFilter $ldap | `   

  30.                SELECT SamAccountName, Department, DisplayName   

  31.                    

  32.             $obj = New-Object psobject   

  33.             $obj | Add-Member NoteProperty SamAccountName $User.SamAccountName   

  34.             $obj | Add-Member NoteProperty Department $User.Department   

  35.             $obj | Add-Member NoteProperty DisplayName $User.DisplayName   

  36.             $obj | Add-Member NoteProperty Size $MailBox.Size   

  37.             $obj | Add-Member NoteProperty TotalItems $MailBox.TotalItems   

  38.             $obj | Add-Member NoteProperty ServerName $MailBox.ServerName   

  39.             $obj | Add-Member NoteProperty StorageGroupName $MailBox.StorageGroupName   

  40.             $obj | Add-Member NoteProperty StoreName $MailBox.StoreName   

  41.             $obj | Add-Member NoteProperty LegacyDN $MailBox.LegacyDN   

  42.             Write-Output $obj  

  43.         }   

  44.     }   

  45.     END   

  46.     {   

  47.         $End = Get-Date  

  48.         Write-Host "Script completed: $end"  

  49.         Write-Host "Total Duration: $($end-$Start).minute"  

  50.     }      

  51. }    

  52. Clear-Host   

  53. $Servers = 'ms01','ms02','ms03','ms04','ms05','ms06'     

  54. $Servers | Get-ExchangeInfo | Export-Csv -path 'c:\ExchangeInfo.csv' -noTypeInformation   

Enjoy!

Tuesday, January 13, 2009

PowerShell and XML

After my last post, I thought it might be a good idea to spend a little more time explaining the XML capabilities of PowerShell. I had 2 people ask for clarification on it so I obviously didn't give it the attention it deserved! Following are a few examples of what can be done with the XML type in PowerShell.

The file that we will be using is from a Microsoft Baseline Security Analysis. To follow along with the examples, it would be beneficial if you had a similar file. You can use this sample. It looks something like this.


The first thing we need to do is explicitly cast it as an XML file:
[XML]$ScanResult = Get-Content 'c:\sampleMBSA.xml'

Now that we have the XML file, lets see what we can do with it.

  1. Get the Server (Machine) name:
    PS> $File.SecScan.Machine
    PS> Test-VP01
  2. Get the IP address:
    PS> $file.SecScan.IP
    PS> 12.345.67.890
  3. What 3 areas were scanned:
    PS> $file.SecScan.Check Select Name
    Name
    Office Security Updates
    SDK Components Security Updates
    Windows Security Updates
  4. See the complete list of updates (installed or not) for Office Security Updates:
    Note that we had 3 areas that were scanned, we are looking at the first one, so we start with [0] index.
    PS> $file.SecScan.Check[0].Detail.UpdateData Select Title
    Title
    Office 2003 Service Pack 2 for Proofing Tools
    Security Update for Office 2003 (KB920813)
    Security Update for Excel 2003 (KB925257)
    Security Update for Word 2003 (KB923094)
    Visio 2003 Service Pack 2
    Security Update for Office 2003 (KB924424)
    ....
  5. See the complete list of updates that need installed for Office Security Updates:
    PS> $file.SecScan.Check[0].Detail.UpdateData `
    > Where{$_.IsInstalled -eq $false} `
    > Select Title
    Office 2003 Service Pack 2
    Security Update for Office 2003 (KB954478)
    Security Update for Office 2003 (KB923272)
    Security Update for PowerPoint 2003 (KB9230
    Security Update for Microsoft Office Excel
    Security Update for Office 2003 (KB936048)
    Security Update for Excel 2003 (KB940602)
    Outlook Live 2003 Service Pack 2
    Project 2003 Service Pack 2
    Security Update for Office 2003 (KB924424)
    ...
  6. See the list of critical updates that need to be installed for Office Security Updates:
    PS> $file.SecScan.Check[0].Detail.UpdateData `
    > where{$_.IsInstalled -eq $false -and $_.Severity -eq 4} `
    > select Title, @{name="Severity";Expression={"Critical"}}
    Severity Title
    Critical Security Update for Access Snapshot Viewer 2003 (KB955439)
    Critical Security Update for Microsoft Office Outlook 2003 (KB945432)

Hopefully, this clarifies some of possibilities with XML and PowerShell.

Enjoy!

Saturday, January 10, 2009

PowerShell & MBSA logs

Was recently asked if I could iterate over a directory containing *.MBSA files from a scheduled run of the Microsoft Baseline Security Analyzer. These *.MBSA files are XML files that contain some basic machine information as well as the number of security and service packs that are required to get the machine to a secure state. The GUI shows the information as follows: The specifics of the request that I received were to grab the count of the following updates per machine (circled in the picture above):
  • SDK Components Security Updates
  • SQL Server Security Updates
  • Windows Security Updates
  • BizTalk Server Security Updates
  • Exchange Security Updates
  • Office Security Updates

What a great opportunity to use PowerShell's XML capabilities! The following script iterates over each file in the directory, parses the file and looks for the count. This information is dynamically entered into an Excel spreadsheet.

  1. # grab our XML files   
  2. $files = Get-ChildItem -path 'C:\SecurityScans'    
  3.   
  4. # Get Excel ready   
  5. $Excel = New-Object -comobject Excel.Application   
  6. $Excel.visible = $True    
  7. $Workbook = $Excel.Workbooks.Add()   
  8. $Info = $Workbook.Worksheets.Item(1)   
  9.   
  10. # Create our column headers   
  11. $Info.Cells.Item(1,1) = "Server name"  
  12. $Info.Cells.Item(1,2) = "SDK Components Security Updates"  
  13. $Info.Cells.Item(1,3) = "SQL Server Security Updates"  
  14. $Info.Cells.Item(1,4) = "Windows Security Updates"  
  15. $Info.Cells.Item(1,5) = "BizTalk Server Security Updates"  
  16. $Info.Cells.Item(1,6) = "Exchange Security Updates"  
  17. $Info.Cells.Item(1,7) = "Office Security Updates"  
  18.   
  19. # Add a little formatting   
  20. $Style = $Info.UsedRange   
  21. $Style.Interior.ColorIndex = 19   
  22. $Style.Font.ColorIndex = 11   
  23. $Style.Font.Bold = $True  
  24.   
  25. $intRow = 2   
  26.   
  27. # iterate over each .mbsa file   
  28. foreach ($file in $files)   
  29. {   
  30.     [XML]$ScanResult = Get-Content $file  
  31.     $Scanned = $ScanResult.SecScan.Check | select Name, Advice   
  32.     $Server = $ScanResult.SecScan.Machine   
  33.     foreach($Scan in $Scanned)   
  34.     {   
  35.         # if Advice doesn't start with a numeric value then set it equal to 0   
  36.         if$Scan.Advice -match '^(?<Cnt>[0-9]*)'){$Advice=$matches.cnt}    else{$Advice=0}   
  37.            
  38.         $Style.Cells.Item($intRow, 1) = $Server  
  39.            
  40.         switch ($Scan.Name)    
  41.         {   
  42.             "SDK Components Security Updates"   {$Style.Cells.Item($intRow, 2) = $Advice;break}   
  43.             "SQL Server Security Updates"       {$Style.Cells.Item($intRow, 3) = $Advice;break}   
  44.             "Windows Security Updates"          {$Style.Cells.Item($intRow, 4) = $Advice;break}   
  45.             "BizTalk Server Security Updates"   {$Style.Cells.Item($intRow, 5) = $Advice;break}   
  46.             "Exchange Security Updates"         {$Style.Cells.Item($intRow, 6) = $Advice;break}   
  47.             "Office Security Updates"           {$Style.Cells.Item($intRow, 7) = $Advice;break}   
  48.         }   
  49.   
  50.     }   
  51.     $intRow = $intRow + 1   
  52. }  

The result is a nicely formatted Excel spreadsheet that has the total number of updates.

This script hopefully demonstrates a how to use few different PowerShell features:

  • Creating a COM object
  • Using Regular Expressions
  • Using [XML] type

Enjoy

Wednesday, January 7, 2009

Verify PSSnapin (Script vs PrimalForms)

When running a script from the console or the various IDE's, you can check for a specific snap-in with the #requires statement. See ALEKSANDAR's blog post for the complete description.

When running a script as a winform (like through PrimalForms), you may need to attack from a different angle. It may be necessary to capture the fact that a particular snap-in is not installed and present it in the GUI or a messagebox. For this the following may be a better approach:
$snapin = "Quest.ActiveRoles.ADManagement"
if ((get-pssnapin -name $snapin -erroraction silentlycontinue) -eq $null)
{
$statusBar1.text =
"$snapin is not installed"
}
Enjoy!

Monday, January 5, 2009

PrimalForms & PowerShell AD Tool

We are in the middle of a file migration and our policy dictates that as our file shares are moved they need to have the following structure:
  • Access.Neurosurgery.Public.Change
  • Access.Neurosurgery.Public.Full
  • Access.Neurosurgery.Public.Read
So every time a new group is requested, the admin has to create 3 distinct groups, assign the scope & type and add the "Managed By" username. Not hard, but time consuming. This looked like a great candidate for PrimalForms!

The UI is pretty straight forward. It asks for the group name, the scope and the owner.


When the Verify Group button is pressed, we perform a few checks before we create the groups. First we check to see if there are similar groups. In this case, the panel is now visible and the status bar indicates that like groups already exist.


Once we verify the group name, we need to verify that the entered group owner is in fact a legitimate AD object.


In this case, the user is not valid. Once all the data is verified, we give the user one last chance to cancel before the groups are created.


That's about it!

Enjoy!

Saturday, January 3, 2009

Project Euler and PowerShell - Problem 14

The following iterative sequence is defined for the set of positive integers:

n -> n/2 (n is even)
n -> 3n + 1 (n is odd)

Using the rule above and starting with 13, we generate the following sequence:

13 -> 40 -> 20 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1

It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 terms. Although it has not been proved yet (Collatz Problem), it is thought that all starting numbers finish at 1.

Which starting number, under one million, produces the longest chain?

NOTE: Once the chain starts the terms are allowed to go above one million.
$limit=1000000;$max=$longest=0
for($x=3;$x -lt $limit;$x+=2)
{
$number = $x
$count = 1
while ($number -ne 1)
{
if($number%2 -eq 0){$number /=2}
else{$number= 3*$number +1}
$count++
}

if($count -gt $max)
{
$max = $count
$longest=$x
"{0}`t{1}" -f $x,$max
}
}
I may be able to get this to run a little faster by storing the sequence count.

Friday, January 2, 2009

Project Euler and PowerShell - Problem 36

This one looked fun!

The decimal number, 585 = 1001001001 (binary), is palindromic in both bases.
Find the sum of all numbers, less than one million, which are palindromic in base 10 and base 2.
(Please note that the palindromic number, in either base, may not include leading zeros.)

Here is my (non-optimized) solution.
# Sum all numbers less then 1,000,000 that are
#
palindromic in base 10 and base 2
function Reverse-Parameter {
param ([string]$number)
for ($i = $number.length - 1; $i -ge 0; $i--)
{
$b = $b + ($number.substring($i,1))
}
[
decimal]$b
}

$max = 1000000;$sum = 0
for ($i=0;$i -lt $max;$i++)
{
$k = Reverse-Parameter $i
if($i -eq $k)
{
$bi = [Convert]::ToString($i,2)
$rbi = Reverse-Parameter $bi
if($bi -eq $rbi)
{
$sum = $sum + $i
"{0}`t{1}`t`t{2}" -f $i,$bi,$sum
}
}
}

Project Euler and PowerShell - Problem 10

Continuing on with Project Euler, I am momentarily skipping problems 8 (getting that big string into a numeric array still eludes me!) and 9 to quickly do problem 10.

The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.
Find the sum of all the primes below two million.

This is another brute force attack. I am sure there is a more efficient algorithm, but the following gets it done.
function isPrime
{
param ($number)
$isPrime = $true
if($number -lt 2) { $isPrime = $False}
if($number -gt 2 -and $number%2 -eq 0) {$isPrime = $False}
for($i=3;$i -le [math]::Sqrt($number);$i+=2)
{
if($number % $i -eq 0) { $isPrime = $False}
}
$isPrime
}

$limit=2000000
$sum=2
for($i=3; $i -lt $limit;$i+=2)
{
if( isPrime $i)
{
$sum+=$i
"{0}`t{1}" -f $i, $sum
}
}
While this generates the correct answer, it is painfully slow. I will be revisiting this one in an attempt to optimize it.